HextaUI

Shimmer

A band of light that sweeps across text for in-progress states, tinted from the text color and still under reduced motion.

Thinking about your question…

export function ShimmerDemo() {
  return (
    <p className="shimmer text-base text-muted-foreground">
      Thinking about your question…
    </p>
  )
}

Shimmer is a Tailwind utility from shadcn's tailwind.css. The HextaUI theme already imports it, so if you've added the theme or any component, you have it.

pnpm dlx shadcn@latest add https://hextaui.com/r/theme.json

Without the theme, install shadcn and import its CSS after Tailwind.

pnpm add shadcn
app/globals.css
@import "tailwindcss";
@import "shadcn/tailwind.css";
<p className="shimmer text-muted-foreground">Generating response…</p>

Use it on short text that describes work in progress, like an assistant thinking, a file processing or a step that's running. The moving highlight says "still working" without a spinner taking up space beside the words.

The text is painted with a gradient through background-clip: text, and a lighter band slides across it. The band is derived from the text's own color, so it suits muted, foreground or colored text without configuration, and it brightens in dark mode, where a lighter band reads better.

  • Under reduced motion the gradient is removed entirely and the text renders in its normal color.
  • In right-to-left layouts the sweep reverses, so it still travels in reading direction.
  • The text stays real text: selectable, searchable and read normally by screen readers.

Options

Speed, band width, color and angle are each one class.

Faster: shimmer-duration-1000

Wider band: shimmer-spread-24

Tinted: shimmer-color-info

Steeper: shimmer-angle-45

export function ShimmerOptions() {
  return (
    <div className="flex flex-col items-start gap-4 text-sm">
      <p className="shimmer text-muted-foreground shimmer-duration-1000">
        Faster: shimmer-duration-1000
      </p>
      <p className="shimmer text-muted-foreground shimmer-spread-24">
        Wider band: shimmer-spread-24
      </p>
      <p className="shimmer text-foreground shimmer-color-info">
        Tinted: shimmer-color-info
      </p>
      <p className="shimmer text-muted-foreground shimmer-angle-45">
        Steeper: shimmer-angle-45
      </p>
    </div>
  )
}

Status updates

Shimmer while a task runs, then sweep once with shimmer-once when it finishes. Changing the key restarts the sweep for each new step.

Ready

"use client"

import * as React from "react"
import { cn } from "cn"

import { Button } from "@/components/ui/button"

const steps = ["Reading files…", "Planning changes…", "Writing code…"]

export function ShimmerStatus() {
  const [step, setStep] = React.useState<number | null>(null)

  React.useEffect(() => {
    if (step === null || step >= steps.length) {
      return
    }
    const timer = setTimeout(() => setStep(step + 1), 1600)
    return () => clearTimeout(timer)
  }, [step])

  const done = step !== null && step >= steps.length

  return (
    <div className="flex flex-col items-center gap-4">
      <p
        key={step ?? "idle"}
        role="status"
        className={cn(
          "h-5 text-sm text-muted-foreground",
          step !== null && "shimmer",
          done && "shimmer-duration-1200 shimmer-once"
        )}
      >
        {step === null ? "Ready" : done ? "Done." : steps[step]}
      </p>
      <Button variant="outline" size="sm" onClick={() => setStep(0)}>
        Run task
      </Button>
    </div>
  )
}
<p className="shimmer md:shimmer-none">Generating response…</p>
<p className="shimmer shimmer-reverse">Undoing…</p>
  • Keep it to a line or two. A shimmering paragraph is tiring to read.
  • Put the text in a role="status" region when it changes, so the new step is announced. The shimmer itself is decoration.
  • For placeholder blocks rather than text, use Skeleton, which shimmers surfaces with the theme's animate-shimmer.
ClassDescription
shimmerSweeps a highlight across the text, every 2s.
shimmer-onceSweeps once instead of repeating.
shimmer-reverseSweeps in the opposite direction.
shimmer-noneTurns it off, for example at a breakpoint.
shimmer-color-<color>Color of the highlight, from the theme or arbitrary. Takes an opacity modifier.
shimmer-duration-<ms>Length of one sweep in milliseconds.
shimmer-spread-<number>Width of the band on the spacing scale, or any length. Defaults to 3ch + 40px.
shimmer-angle-<deg>Tilt of the band. Defaults to 20°.