shimmer-trace 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,424 @@
1
+ # shimmer-trace ✨
2
+
3
+ > **Automatic skeleton loaders that trace your real UI.**
4
+ > Zero configuration. Zero layout shift. One line of code.
5
+
6
+ [![npm version](https://img.shields.io/npm/v/shimmer-trace)](https://www.npmjs.com/package/shimmer-trace)
7
+ [![bundle size](https://img.shields.io/badge/min%2Bgzip-3.29%20kB-brightgreen)](#bundle-size)
8
+ [![bundlephobia](https://img.shields.io/bundlephobia/minzip/shimmer-trace?label=bundlephobia)](https://bundlephobia.com/package/shimmer-trace)
9
+ [![React 18+](https://img.shields.io/badge/React-18%2B-61dafb)](https://react.dev)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-ready-3178c6)](https://www.typescriptlang.org)
11
+ [![license](https://img.shields.io/npm/l/shimmer-trace)](./LICENSE)
12
+
13
+ <p align="center">
14
+ <img src="https://raw.githubusercontent.com/jeetvora331/shimmer-trace/main/assets/demo.gif" alt="shimmer-trace demo" width="720" />
15
+ </p>
16
+
17
+ ---
18
+
19
+ Most skeleton libraries make you **hand-draw** a skeleton that matches your UI.
20
+ You measure heights, pick widths, match border-radii, and pray nothing changes.
21
+
22
+ **shimmer-trace does none of that.**
23
+
24
+ It renders your real component invisibly, traces every element's exact position and size from the live DOM, then paints a pixel-perfect shimmer overlay on top — automatically, on every resize.
25
+
26
+ ```tsx
27
+ // Before: manual skeleton hell
28
+ <SkeletonRect width="100%" height={24} borderRadius={8} />
29
+ <SkeletonRect width="60%" height={16} borderRadius={4} />
30
+ <SkeletonCircle size={48} />
31
+
32
+ // After: just wrap it
33
+ <Shimmer loading={loading}>
34
+ <UserCard />
35
+ </Shimmer>
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Features
41
+
42
+ - **Auto-tracing** — Measures real DOM layout. No manual skeleton code.
43
+ - **Zero CLS** — Container layout preserved. Default `preserveBackground` keeps card backgrounds, borders, and padding visible underneath the shimmer.
44
+ - **Synchronized animation** — One overlay, one wave. All skeletons animate in perfect sync.
45
+ - **5 animation styles** — `wave`, `pulse`, `shine`, `glow`, `gradient`.
46
+ - **List mode** — `dummyLength` clones your list items for skeleton lists, with template caching.
47
+ - **Suspense-native** — `ShimmerSuspense` wraps any suspended component with no `loading` prop.
48
+ - **Factory pattern** — `createShimmer` pre-bakes your config. Use it like a component everywhere.
49
+ - **Composable** — Nested `Shimmer` components bubble their rects up to a single master overlay.
50
+ - **ResizeObserver** — Re-traces automatically when the container resizes.
51
+ - **3.29 kB min+gzip** (2.95 kB brotli) — Zero runtime dependencies. Run `npm run size` to verify.
52
+ - **TypeScript-first** — Full types included.
53
+
54
+ ---
55
+
56
+ ## Install
57
+
58
+ ```bash
59
+ npm install shimmer-trace
60
+ # or
61
+ yarn add shimmer-trace
62
+ # or
63
+ pnpm add shimmer-trace
64
+ ```
65
+
66
+ **Peer dependencies:** React 18+
67
+
68
+ ---
69
+
70
+ ## Quick Start
71
+
72
+ ```tsx
73
+ import { Shimmer } from "shimmer-trace";
74
+
75
+ function ProfilePage() {
76
+ const [loading, setLoading] = useState(true);
77
+
78
+ return (
79
+ <Shimmer loading={loading}>
80
+ <UserCard />
81
+ </Shimmer>
82
+ );
83
+ }
84
+ ```
85
+
86
+ That's it. `shimmer-trace` walks the DOM inside `<UserCard />`, finds every text node, image, input, and button, and draws a shimmer skeleton that matches it exactly.
87
+
88
+ ---
89
+
90
+ ## API Reference
91
+
92
+ ### `<Shimmer>`
93
+
94
+ The core component. Wrap anything with it.
95
+
96
+ ```tsx
97
+ <Shimmer
98
+ loading={boolean} // required — controls shimmer on/off
99
+ animation="wave" // 'wave' | 'pulse' | 'shine' | 'glow' | 'gradient'
100
+ baseColor="#e0e0e0" // skeleton base color
101
+ highlightColor="#f5f5f5" // shimmer highlight color
102
+ speed={1.5} // animation duration in seconds
103
+ borderRadius="4px" // override auto-detected border-radius
104
+ dummyLength={10} // list mode: number of skeleton items
105
+ stopPropagation={false} // force this Shimmer to be a master
106
+ className="my-class" // applied to the container div
107
+ style={{ display: "flex" }} // merged into container styles
108
+ >
109
+ {children}
110
+ </Shimmer>
111
+ ```
112
+
113
+ | Prop | Type | Default | Description |
114
+ | -------------------- | ------------------------------------------------------ | ----------- | --------------------------------------------------- |
115
+ | `loading` | `boolean` | `false` | Enables the shimmer skeleton |
116
+ | `animation` | `'wave' \| 'pulse' \| 'shine' \| 'glow' \| 'gradient'` | `'wave'` | Animation style |
117
+ | `preserveBackground` | `boolean` | `true` | Keep card backgrounds/borders visible while loading |
118
+ | `baseColor` | `string` | `'#e0e0e0'` | Base skeleton color |
119
+ | `highlightColor` | `string` | `'#f5f5f5'` | Shimmer highlight color |
120
+ | `speed` | `number` | `1.5` | Animation speed in seconds |
121
+ | `borderRadius` | `string` | auto | Override border-radius on all blocks |
122
+ | `dummyLength` | `number` | — | Enables list mode (see below) |
123
+ | `stopPropagation` | `boolean` | `false` | Force master renderer, ignore parent context |
124
+ | `className` | `string` | — | Class on the container `<div>` |
125
+ | `style` | `CSSProperties` | — | Inline styles on the container `<div>` |
126
+
127
+ ---
128
+
129
+ ## Examples
130
+
131
+ ### 1. Profile Card
132
+
133
+ Wrap any component — shimmer-trace handles the rest.
134
+
135
+ ```tsx
136
+ import { Shimmer } from "shimmer-trace";
137
+
138
+ function App() {
139
+ const [loading, setLoading] = useState(true);
140
+
141
+ return (
142
+ <Shimmer loading={loading}>
143
+ <div className="profile-card">
144
+ <img src={user.avatar} alt="Avatar" />
145
+ <div>
146
+ <h3>{user.name}</h3>
147
+ <span>{user.role}</span>
148
+ <p>{user.bio}</p>
149
+ </div>
150
+ </div>
151
+ </Shimmer>
152
+ );
153
+ }
154
+ ```
155
+
156
+ ### 2. Form Skeleton
157
+
158
+ Works out of the box with inputs, labels, and buttons.
159
+
160
+ ```tsx
161
+ <Shimmer loading={loading}>
162
+ <form>
163
+ <label>Email</label>
164
+ <input type="email" placeholder="you@example.com" />
165
+
166
+ <label>Message</label>
167
+ <textarea placeholder="Your message..." />
168
+
169
+ <button type="submit">Send</button>
170
+ </form>
171
+ </Shimmer>
172
+ ```
173
+
174
+ ### 3. List Skeleton with `dummyLength`
175
+
176
+ Loading a list from an API? `dummyLength` clones your list item template to show the right number of skeleton rows — even when the array is empty.
177
+
178
+ ```tsx
179
+ <Shimmer loading={loading} dummyLength={10}>
180
+ {posts.map((post) => (
181
+ <div className="post-row" key={post.id}>
182
+ <img src={post.thumbnail} alt="" />
183
+ <div>
184
+ <h4>{post.title}</h4>
185
+ <span>{post.author}</span>
186
+ </div>
187
+ <span className="badge">{post.category}</span>
188
+ </div>
189
+ ))}
190
+ </Shimmer>
191
+ ```
192
+
193
+ **How it works:**
194
+
195
+ - `loading=false` → renders your `.map()` output normally
196
+ - `loading=true` → grabs the first list item, clones it `dummyLength` times, and shimmers it
197
+ - If your array is empty during loading (e.g., `posts = []`), shimmer-trace uses a **cached template** from the previous render — the skeleton always matches your real layout
198
+
199
+ ### 4. Synchronized Flex Layout
200
+
201
+ One `<Shimmer>` wraps multiple cards. One overlay. One perfectly synchronized wave.
202
+
203
+ ```tsx
204
+ <Shimmer loading={loading} style={{ display: "flex", gap: "1rem" }}>
205
+ <StatCard value="4,821" label="Total Users" />
206
+ <StatCard value="98.4%" label="Uptime" />
207
+ <StatCard value="142ms" label="Avg Latency" />
208
+ </Shimmer>
209
+ ```
210
+
211
+ No separate shimmers per card. One master overlay covers them all — the wave sweeps the entire row in sync.
212
+
213
+ ### 5. Custom Colors (Dark Mode)
214
+
215
+ ```tsx
216
+ <Shimmer loading={loading} baseColor="#1e1e3a" highlightColor="#2d2d52">
217
+ <DashboardWidget />
218
+ </Shimmer>
219
+ ```
220
+
221
+ ---
222
+
223
+ ## `createShimmer` — Factory Pattern
224
+
225
+ Pre-configure once, use everywhere. Great for design systems.
226
+
227
+ ```tsx
228
+ import { createShimmer } from "shimmer-trace";
229
+
230
+ // Create a pre-configured Shimmer component
231
+ const DarkShimmer = createShimmer({
232
+ baseColor: "#1e1e3a",
233
+ highlightColor: "#2d2d52",
234
+ animation: "wave",
235
+ speed: 1.2,
236
+ });
237
+
238
+ // Use it like a regular component — just add `loading`
239
+ function App() {
240
+ return (
241
+ <DarkShimmer loading={loading}>
242
+ <UserCard />
243
+ </DarkShimmer>
244
+ );
245
+ }
246
+ ```
247
+
248
+ The created component accepts all the same props as `<Shimmer>` — the factory defaults are just overridable.
249
+
250
+ ---
251
+
252
+ ## `ShimmerSuspense` — Suspense-Native Loading
253
+
254
+ No `loading` prop. No state. Shimmer shows automatically while children are suspended.
255
+
256
+ ```tsx
257
+ import { ShimmerSuspense } from "shimmer-trace";
258
+ ```
259
+
260
+ ### Option A: Explicit template (recommended)
261
+
262
+ Pass the same component without data as `template`. Zero shimmer-awareness needed in your component.
263
+
264
+ ```tsx
265
+ function UserCard({ user }) {
266
+ return (
267
+ <div className="card">
268
+ <img src={user.avatar} alt="" />
269
+ <h3>{user.name}</h3>
270
+ <p>{user.bio}</p>
271
+ </div>
272
+ );
273
+ }
274
+
275
+ // Same shape, no data — used as skeleton template
276
+ const UserCardSkeleton = () => (
277
+ <div className="card">
278
+ <img src="" alt="" />
279
+ <h3>&nbsp;</h3>
280
+ <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
281
+ </div>
282
+ );
283
+
284
+ <ShimmerSuspense template={<UserCardSkeleton />}>
285
+ <UserCard resource={resource} />
286
+ </ShimmerSuspense>;
287
+ ```
288
+
289
+ ### Option B: `useIsShimmering` hook
290
+
291
+ No template needed. The component detects shimmer mode and renders an empty shape.
292
+
293
+ ```tsx
294
+ import { useIsShimmering } from "shimmer-trace";
295
+
296
+ function UserCard({ resource }) {
297
+ const isShimmering = useIsShimmering();
298
+
299
+ // Skip data fetching in shimmer mode (avoids nested Suspense throw)
300
+ const user = isShimmering ? null : resource.read();
301
+
302
+ return (
303
+ <div className="card">
304
+ <img src={user?.avatar ?? ""} alt="" />
305
+ <h3>{user?.name ?? " "}</h3>
306
+ <p>{user?.bio ?? " "}</p>
307
+ </div>
308
+ );
309
+ }
310
+
311
+ <ShimmerSuspense>
312
+ <UserCard resource={resource} />
313
+ </ShimmerSuspense>;
314
+ ```
315
+
316
+ `ShimmerSuspense` accepts all `ShimmerConfig` props too:
317
+
318
+ ```tsx
319
+ <ShimmerSuspense
320
+ template={<UserCardSkeleton />}
321
+ animation="pulse"
322
+ baseColor="#1e1e3a"
323
+ highlightColor="#2d2d52"
324
+ >
325
+ <UserCard resource={resource} />
326
+ </ShimmerSuspense>
327
+ ```
328
+
329
+ ---
330
+
331
+ ## Composing Nested Shimmers
332
+
333
+ `Shimmer` components nest intelligently. Inner (Reporter) shimmers report their rects to the nearest outer (Master) shimmer — all combined into a single overlay.
334
+
335
+ ```tsx
336
+ <Shimmer loading={loading}>
337
+ <PageHeader>
338
+ {/* This nested Shimmer contributes its rects to the parent overlay */}
339
+ <Shimmer loading={loading}>
340
+ <NavigationMenu />
341
+ </Shimmer>
342
+ </PageHeader>
343
+ <MainContent />
344
+ </Shimmer>
345
+ ```
346
+
347
+ Use `stopPropagation` to force an independent shimmer:
348
+
349
+ ```tsx
350
+ <Shimmer loading={outerLoading}>
351
+ <Sidebar />
352
+
353
+ {/* Independent shimmer — own overlay, own animation */}
354
+ <Shimmer loading={innerLoading} stopPropagation>
355
+ <Feed />
356
+ </Shimmer>
357
+ </Shimmer>
358
+ ```
359
+
360
+ ---
361
+
362
+ ## DOM Control Attributes
363
+
364
+ Fine-tune what gets traced with data attributes:
365
+
366
+ ```tsx
367
+ // Trace this specific element (overrides auto-detection)
368
+ <div data-shimmer>Custom block</div>
369
+
370
+ // Skip this element entirely
371
+ <div data-shimmer-ignore>Never shimmer this</div>
372
+ ```
373
+
374
+ ---
375
+
376
+ ## How It Works
377
+
378
+ 1. **Render real DOM** — `Shimmer` renders children normally. With `preserveBackground` (default), CSS rules hide text (`color: transparent`) and media (`opacity: 0`) on leaf elements while keeping container backgrounds, borders, and padding fully visible. Layout stays identical — zero CLS.
379
+
380
+ 2. **Walk the DOM** — `useTrace` recursively traverses the container, collecting every traceable element: headings, paragraphs, images, inputs, buttons, and leaf nodes with visible dimensions.
381
+
382
+ 3. **Measure everything** — Each element is measured with `getBoundingClientRect()` relative to the master container, capturing position, size, and computed `border-radius`.
383
+
384
+ 4. **Build the overlay** — One absolutely-positioned `<div>` is rendered per traced rect, sized and positioned to match exactly. For sweep animations (`wave`, `shine`), each block also gets a gradient layer that spans the full container width — the highlight sweeps across all blocks in perfect sync.
385
+
386
+ 5. **ResizeObserver** — Container resize triggers an automatic re-trace, so the skeleton stays pixel-perfect on responsive layouts.
387
+
388
+ 6. **Re-trace on resize** — `ResizeObserver` watches the container and re-measures on every resize, keeping skeletons accurate at any screen size.
389
+
390
+ ---
391
+
392
+ ## TypeScript
393
+
394
+ Full types exported:
395
+
396
+ ```ts
397
+ import type {
398
+ ShimmerProps, // Props for <Shimmer>
399
+ ShimmerConfig, // Config options (colors, speed, animation)
400
+ ShimmerRect, // Measured element rectangle
401
+ AnimationType, // 'wave' | 'pulse' | 'breathe'
402
+ ShimmerSuspenseProps,
403
+ } from "shimmer-trace";
404
+ ```
405
+
406
+ ---
407
+
408
+ ## Comparison
409
+
410
+ | | shimmer-trace | react-loading-skeleton | MUI Skeleton |
411
+ | ---------------------- | ---------------- | ---------------------- | -------------- |
412
+ | Manual skeleton code | ❌ None | ✅ Required | ✅ Required |
413
+ | Matches real layout | ✅ Automatically | ⚠️ Manual | ⚠️ Manual |
414
+ | List mode | ✅ `dummyLength` | ❌ | ❌ |
415
+ | Suspense support | ✅ Native | ❌ | ❌ |
416
+ | Synchronized animation | ✅ One overlay | ⚠️ Per-element | ⚠️ Per-element |
417
+ | Zero layout shift | ✅ | ⚠️ | ⚠️ |
418
+ | Bundle size | ~3kb | ~5kb | ~12kb |
419
+
420
+ ---
421
+
422
+ ## License
423
+
424
+ ISC — [Jeet Vora](https://github.com/jeetvora331)
@@ -0,0 +1,224 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+ import React__default, { ReactNode, RefObject } from 'react';
4
+
5
+ /**
6
+ * Represents a measured rectangle of a traced DOM element,
7
+ * positioned relative to the Master Shimmer container.
8
+ */
9
+ interface ShimmerRect {
10
+ x: number;
11
+ y: number;
12
+ width: number;
13
+ height: number;
14
+ borderRadius: string;
15
+ }
16
+ /** Available animation types for the shimmer effect. */
17
+ type AnimationType = 'wave' | 'pulse' | 'shine' | 'glow' | 'gradient';
18
+ /** Configuration options for the shimmer effect (all optional). */
19
+ interface ShimmerConfig {
20
+ /** Animation style. Defaults to 'wave'. */
21
+ animation?: AnimationType;
22
+ /** Base color of the shimmer blocks. Defaults to '#e0e0e0'. */
23
+ baseColor?: string;
24
+ /** Highlight color of the shimmer animation. Defaults to '#f5f5f5'. */
25
+ highlightColor?: string;
26
+ /** Animation duration in seconds. Defaults to 1.5. */
27
+ speed?: number;
28
+ /** Global border-radius override. If omitted, auto-detected from each element (defaults to 4px if detection is 0px). */
29
+ borderRadius?: string;
30
+ /**
31
+ * Keep container backgrounds, borders, and padding visible while loading.
32
+ * When `true` (default), only text and media leaves are hidden via
33
+ * `color:transparent` / `opacity:0` so card backgrounds, borders, and
34
+ * spacing remain visible underneath the shimmer overlay.
35
+ *
36
+ * Set `false` for legacy behavior (`visibility:hidden` on whole tree).
37
+ */
38
+ preserveBackground?: boolean;
39
+ }
40
+ /** Props for the Shimmer component. */
41
+ interface ShimmerProps extends ShimmerConfig {
42
+ /** Whether the loading state is active. */
43
+ loading?: boolean;
44
+ /** The children to trace and render shimmer over. */
45
+ children: ReactNode;
46
+ /**
47
+ * Number of placeholder clones to generate for list-like loading states.
48
+ *
49
+ * When `loading=true` and `dummyLength` is set, Shimmer grabs the first
50
+ * available child (or a cached template from the last loaded render) and
51
+ * clones it `dummyLength` times to produce skeleton placeholders.
52
+ *
53
+ * When `loading=false`, children are rendered as-is.
54
+ */
55
+ dummyLength?: number;
56
+ /**
57
+ * Props injected into each child element while `loading=true` so the
58
+ * skeleton renders with realistic shape without requiring real data.
59
+ *
60
+ * Example:
61
+ * ```tsx
62
+ * <Shimmer
63
+ * loading={loading}
64
+ * dummyData={{ user: { name: 'Loading...', role: '...', avatar: '' } }}
65
+ * >
66
+ * <UserCard user={user} />
67
+ * </Shimmer>
68
+ * ```
69
+ *
70
+ * While loading, each direct child is cloned with these props merged on top
71
+ * of its own props. Ignored when `loading=false`.
72
+ */
73
+ dummyData?: Record<string, any>;
74
+ /**
75
+ * Component used to auto-generate skeleton elements while `loading=true`.
76
+ *
77
+ * When set, Shimmer ignores `children` during loading and renders
78
+ * `dummyLength` (defaults to 1) instances of `<as {...dummyData} />`
79
+ * to derive shape. Real children render once `loading=false`.
80
+ *
81
+ * ```tsx
82
+ * <Shimmer
83
+ * loading={loading}
84
+ * as={MovieCard}
85
+ * dummyData={{ movie: movieTemplate }}
86
+ * dummyLength={10}
87
+ * >
88
+ * {movies.map((m) => <MovieCard movie={m} key={m.id} />)}
89
+ * </Shimmer>
90
+ * ```
91
+ */
92
+ as?: React__default.ComponentType<any>;
93
+ /** Force this Shimmer to be a Master renderer even if nested inside another Shimmer. */
94
+ stopPropagation?: boolean;
95
+ /**
96
+ * className applied to the Master container div.
97
+ * Use to control layout (e.g. display:flex) without losing position:relative.
98
+ */
99
+ className?: string;
100
+ /**
101
+ * Inline styles merged into the Master container div.
102
+ * position:relative is always applied; everything else is overridable.
103
+ */
104
+ style?: React__default.CSSProperties;
105
+ }
106
+
107
+ /**
108
+ * The main Shimmer component.
109
+ *
110
+ * Auto-detects **Master** (no parent Shimmer) vs **Reporter** (nested).
111
+ * - Master: renders children hidden, traces DOM, paints overlay.
112
+ * - Reporter: measures own rects, reports to parent Master.
113
+ *
114
+ * ### Skeleton shape via `dummyData`
115
+ *
116
+ * Pass `dummyData` so children render with realistic data while loading.
117
+ * No render-prop, no manual `data || fallback` in JSX.
118
+ *
119
+ * ```tsx
120
+ * const userTemplate = { name: 'Loading...', role: '...', avatar: '' };
121
+ *
122
+ * <Shimmer loading={loading} dummyData={{ user: userTemplate }}>
123
+ * <UserCard user={user} />
124
+ * </Shimmer>
125
+ * ```
126
+ *
127
+ * ### List mode (`dummyLength`)
128
+ *
129
+ * Combined with `dummyData`, clones the first child N times with
130
+ * template props merged in:
131
+ *
132
+ * ```tsx
133
+ * <Shimmer
134
+ * loading={loading}
135
+ * dummyLength={5}
136
+ * dummyData={{ fruit: { name: 'xxxxx', price: '$0.00' } }}
137
+ * >
138
+ * <FruitCard fruit={undefined as any} />
139
+ * </Shimmer>
140
+ * ```
141
+ */
142
+ declare function Shimmer({ loading, children, dummyLength, dummyData, as, stopPropagation, animation, baseColor, highlightColor, speed, borderRadius, preserveBackground, className, style, }: ShimmerProps): react_jsx_runtime.JSX.Element;
143
+
144
+ /**
145
+ * Factory function to create a pre-configured Shimmer component.
146
+ * Avoids "Provider Hell" by baking config into the returned component.
147
+ *
148
+ * All config properties are optional — defaults are used for anything
149
+ * not specified.
150
+ *
151
+ * @example
152
+ * ```tsx
153
+ * const AppShimmer = createShimmer({
154
+ * animation: 'pulse',
155
+ * baseColor: '#1a1a2e',
156
+ * highlightColor: '#16213e',
157
+ * speed: 2,
158
+ * });
159
+ *
160
+ * <AppShimmer loading={isLoading}>
161
+ * <MyComponent />
162
+ * </AppShimmer>
163
+ * ```
164
+ */
165
+ declare function createShimmer(config?: ShimmerConfig): (props: Omit<ShimmerProps, keyof ShimmerConfig> & Partial<ShimmerConfig>) => react_jsx_runtime.JSX.Element;
166
+
167
+ interface ShimmerContextValue {
168
+ register: (id: string, rects: ShimmerRect[]) => void;
169
+ unregister: (id: string) => void;
170
+ /** Ref object (not .current) so Reporters always read a fresh value. */
171
+ masterRef: RefObject<HTMLElement | null>;
172
+ loading: boolean;
173
+ config: Required<ShimmerConfig>;
174
+ }
175
+ declare const ShimmerContext: React.Context<ShimmerContextValue | null>;
176
+ declare function useShimmerContext(): ShimmerContextValue | null;
177
+ declare function useIsShimmering(): boolean;
178
+
179
+ interface ShimmerSuspenseProps extends ShimmerConfig {
180
+ children: React__default.ReactNode;
181
+ /**
182
+ * Explicit skeleton template. Rendered hidden and traced for shimmer shape.
183
+ *
184
+ * Preferred — pass the same component with no data props:
185
+ * ```tsx
186
+ * <ShimmerSuspense template={<UserCard />}>
187
+ * <UserCard />
188
+ * </ShimmerSuspense>
189
+ * ```
190
+ *
191
+ * If omitted, falls back to Option B: children are re-rendered with
192
+ * `useIsShimmering()=true` so they can return an empty shape themselves.
193
+ */
194
+ template?: React__default.ReactNode;
195
+ }
196
+ /**
197
+ * Suspense boundary that automatically shows a shimmer skeleton while
198
+ * children are suspended (e.g. useSuspenseQuery, use(promise), etc).
199
+ *
200
+ * **Option A — explicit template (preferred):**
201
+ * ```tsx
202
+ * <ShimmerSuspense template={<UserCard />}>
203
+ * <UserCard />
204
+ * </ShimmerSuspense>
205
+ * ```
206
+ *
207
+ * **Option B — useIsShimmering hook (no template):**
208
+ * ```tsx
209
+ * function UserCard() {
210
+ * const isShimmering = useIsShimmering();
211
+ * const data = isShimmering ? null : useSuspenseQuery(...);
212
+ * return <div><h3>{data?.name}</h3></div>;
213
+ * }
214
+ *
215
+ * <ShimmerSuspense>
216
+ * <UserCard />
217
+ * </ShimmerSuspense>
218
+ * ```
219
+ * Components must use `useIsShimmering()` to skip data fetching in shimmer mode,
220
+ * otherwise they will also suspend inside the fallback (causing an empty skeleton).
221
+ */
222
+ declare function ShimmerSuspense({ children, template, ...shimmerConfig }: ShimmerSuspenseProps): react_jsx_runtime.JSX.Element;
223
+
224
+ export { type AnimationType, Shimmer, type ShimmerConfig, ShimmerContext, type ShimmerProps, type ShimmerRect, ShimmerSuspense, type ShimmerSuspenseProps, createShimmer, useIsShimmering, useShimmerContext };