Shimmer

Animated highlight sweeping across content.

A highlight that sweeps through text and content, for loading, thinking and streaming states.

The content is used as a mask and a gradient band is swept behind it on the UI thread, so the highlight is clipped to the glyph shapes — the letters themselves catch the light rather than sitting under a passing overlay.

The sweep is clipped to the glyphs, so the letters catch the light rather than a box over them.

Installation

Shimmer ships with the library — no separate install.

import { Shimmer, Skeleton } from 'panelui-native';

Or copy the source into your project, to own and edit it:

npx panelui-cli@latest add shimmer

Usage

<Shimmer>Thinking…</Shimmer>

<Shimmer duration={1400} textClassName="text-lg font-medium">
  Generating response…
</Shimmer>

// Mask arbitrary content instead of a string.
<Shimmer as="view" className="w-full gap-2">
  <Skeleton className="h-4 w-3/4" />
  <Skeleton className="h-4 w-full" />
</Shimmer>

Examples

A thinking indicator

The default. The sweep is clipped to the glyphs, so the letters catch the light instead of sitting under a passing band.

<Shimmer>Thinking…</Shimmer>

<Shimmer duration={1400} textClassName="text-lg font-medium">
  Generating response…
</Shimmer>

Masking a subtree

as="view" masks the sweep to whatever you pass instead of to a string.

<Shimmer as="view" className="w-full gap-2">
  <Skeleton className="h-4 w-3/4" />
  <Skeleton className="h-4 w-full" />
  <Skeleton className="h-4 w-1/2" />
</Shimmer>

Tuning the sweep

{/* Reverses on each pass instead of restarting from the left. */}
<Shimmer mode="ping-pong" duration={1600}>Ping-pong</Shimmer>

{/* Right to left. */}
<Shimmer reverse>Reversed</Shimmer>

{/* A wider, slower band. */}
<Shimmer spread={4} duration={2600}>Slow and broad</Shimmer>

{/* One pass, then stop. */}
<Shimmer once>Sweeps once</Shimmer>

Custom colours

Both default to theme tokens, so you only need these on a surface the tokens do not suit.

<Shimmer baseColor="#3f3f46" shimmerColor="#fafafa">
  Neutral on dark
</Shimmer>

Turning it off

Renders the content statically in baseColor. Useful for stopping the sweep the moment a stream finishes, without swapping components.

<Shimmer enabled={isStreaming}>{status}</Shimmer>

API Reference

Shimmer

PropTypeDefaultDescription
classNamestring
as'text' | 'view''text'text renders children as a single styled string and masks the sweep to the glyphs. view masks the sweep to whatever subtree you pass.
durationnumber2000Milliseconds for one sweep.
spreadnumber2Width of the highlight band, as a multiple of the content width.
baseColorstringColour of the content at rest. Defaults to the theme's muted foreground.
shimmerColorstringColour at the centre of the sweep. Defaults to the theme's foreground.
mode'loop' | 'ping-pong''loop'loop restarts from the left; ping-pong reverses on each pass.
oncebooleanfalseSweep once instead of repeating.
reversebooleanfalseSweep right-to-left.
enabledbooleantrueSet false to render the content statically without animating.
textClassNamestringExtra classes for the text when as="text".
textStyleStyleProp<TextStyle>Extra styles for the text when as="text".
colorstringDeprecated. Use shimmerColor.
intensitynumberPeak opacity of the highlight. Deprecated. Set shimmerColor to a colour with the alpha you want.

Every part also accepts the underlying React Native props (ViewProps or TextProps) and a className for Tailwind utilities.

Notes

The sweep runs entirely on the UI thread and never re-renders React while animating.

It stops when the OS reduce-motion setting is on, and when enabled is false — in both cases the content renders statically in baseColor.

baseColor and shimmerColor default to the theme's muted-foreground and foreground tokens, so the effect reads correctly on any surface without configuration. color and intensity still work but are deprecated in favour of the two colour props.

On this page