Skip to content
Textlab
1
Browse

ComponentsReveal

Character reveal

Letters arrive one at a time.

Every letter lands

Installation

$ npx shadcn@latest add @textlab/char-reveal

Needs @textlab in your components.json. Setup takes a minute.

Usage

usage.tsx
import { CharReveal } from "@/components/textlab/char-reveal/char-reveal";

<CharReveal variant="drop" text="Hello there" />

Variants

fadedropspinscale

Props

PropTypeDefaultDescription
text*string–The copy.
variant"fade" | "drop" | "spin" | "scale""fade"fade is quiet, drop falls into place, spin turns up from the baseline, scale pops up from small.
asElementType"span"Rendered element.
trigger"mount" | "inView" | "hover" | "manual""inView"What starts the animation. manual follows `active`.
activebooleanfalseDrives trigger="manual".
oncebooleantrueIn view: play once instead of on every entry.
delaynumber0Seconds before the first part moves.
staggernumber0.02Seconds between parts.
durationnumber0.6 (fade), 0.7Seconds each part takes.
easeEaseName | [number, number, number, number]"enter"Easing token or cubic bezier.
reducedMotion"user" | "always" | "never""user"user follows the OS setting; always shows the still version.
classNamestring–Merged onto the root.
onComplete() => void–Called when a one-shot animation finishes.

When to use it

Reach for it

  • Short display words and names.
  • Moments where the arrival itself is the point.

Skip it

  • Anything over ~60 letters; the tail gets long.
  • Text people need to read quickly.

Accessibility and motion

Accessibility

  • Letters are aria-hidden; the word is read once.
  • Grapheme-aware, so emoji and accents never split.

Motion

  • 20 ms between letters; the total stays under a second for most words.
  • Words are nowrap, so lines never jump while letters move.
Reduced motion. Shows the text immediately.

Performance

  • One motion span per letter. Fine for headings, not paragraphs.