Text with a band of light sweeping across it, from muted to full foreground color. Suited to AI "thinking" and loading status lines; static under reduced motion.
Reading 3 files in src/billing
import { Sparkles } from "lucide-react"
import { ShimmerText } from "@/components/ballmac/shimmer-text"
export default function ShimmerTextDemo() {
return (
<div className="w-full max-w-sm rounded-xl border bg-card p-4 text-card-foreground">
<div className="flex items-center gap-2 text-sm" role="status" aria-live="polite">
<Sparkles className="size-4 text-muted-foreground" aria-hidden="true" />
<ShimmerText className="font-medium">Thinking…</ShimmerText>
</div>
<p className="mt-2 ps-6 font-mono text-xs text-muted-foreground">Reading 3 files in src/billing</p>
</div>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/shimmer-textInstall the dependencies.
$ pnpm add motion@^12Copy the source into your project.
components/ballmac/shimmer-text.tsx// Ballmac UI: Shimmer Text. https://ui.ballmac.com/components/shimmer-text "use client" import * as React from "react" import { animate, useReducedMotion } from "motion/react" import { cn } from "@/lib/utils" type ShimmerTextElement = "span" | "p" | "div" | "h1" | "h2" | "h3" | "h4" type ShimmerTextProps = React.ComponentProps<"span"> & { /** The element to render. Defaults to "span". */ as?: ShimmerTextElement /** Seconds for one sweep across the text. */ duration?: number /** Half-width of the highlight in em, so it scales with the font size. */ spread?: number } function ShimmerText({ as = "span", duration = 2, spread = 2, className, style, children, ...props }: ShimmerTextProps) { const Comp = as as React.ElementType const ref = React.useRef<HTMLElement>(null) const reduceMotion = useReducedMotion() React.useEffect(() => { const node = ref.current if (!node || reduceMotion) return // The gradient is 250% wide: at 100% the highlight sits left of the text, at 0% right of it. const controls = animate( node, { backgroundPosition: ["100% 0%", "0% 0%"] }, { duration, ease: "linear", repeat: Infinity } ) return () => { controls.stop() node.style.backgroundPosition = "" } }, [duration, reduceMotion]) return ( <Comp ref={ref} data-slot="shimmer-text" className={cn( "inline-block bg-clip-text bg-no-repeat text-transparent forced-colors:bg-none forced-colors:text-[CanvasText]", className )} style={{ // Outside the highlight the text is --muted-foreground, which keeps body-text contrast. backgroundImage: `linear-gradient(90deg, var(--muted-foreground) calc(50% - ${spread}em), var(--foreground) 50%, var(--muted-foreground) calc(50% + ${spread}em))`, backgroundSize: "250% 100%", backgroundPosition: "100% 0%", ...style, }} {...props} > {children} </Comp> ) } export { ShimmerText, type ShimmerTextProps }Update the import paths to match your project setup.
Usage
import { ShimmerText } from "@/components/ballmac/shimmer-text"The full example is in the Code tab above.
API reference
| Prop | Type | Default |
|---|---|---|
asThe element to render. Defaults to "span". | ShimmerTextElement | "span" |
durationSeconds for one sweep across the text. | number | 2 |
spreadHalf-width of the highlight in em, so it scales with the font size. | number | 2 |
Also accepts the standard attributes of its root element.
Use with AI
Wrap short text: <ShimmerText>Thinking…</ShimmerText>. The base color is --muted-foreground, so the text stays readable at every frame; the sweep brightens it to --foreground. In forced-colors mode it falls back to plain system text. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Shimmer Text (@ballmac/shimmer-text) to this project with the shadcn MCP, then use it where it fits.
Use it for
- An AI assistant's "Thinking…" or "Searching the docs…" line while a response streams
- Inline pending states such as "Deploying…" or "Indexing 1,204 files"
- A subtle accent on a short marketing label
Not for
- Paragraphs or long text (the sweep becomes distracting)
- The only signal that something is loading; pair it with aria-live or a spinner's label for screen readers
Registry JSON: https://ui.ballmac.com/r/shimmer-text.json
Credits
Free to use in personal and commercial projects.
- npm
- motion@^12
- Registry
- shadcn/utils
Pairs well with
Text Reveal
Reveals a headline or paragraph word by word or character by character with a soft blur and rise, when it scrolls into view or on mount.
Border Beam
A short beam of light that travels around the inside edge of its parent's border at a constant speed. Decorative, token-colored, hidden under reduced motion.
Animated Beam
An SVG beam that connects two elements with a curved path and sends a glowing gradient pulse along it. Follows layout changes, pauses off-screen, static under reduced motion.
Animated Grid
A decorative hairline grid background where a few random cells softly light up and fade, like instrument lights. SVG, hydration-safe, static under reduced motion.