Scramble text
Free · MITText scrambles through random glyphs and resolves left to right on mount, on view, on hover or on demand; renders the real text on the server.
Installation
$ npx shadcn@latest add https://goodui.dev/r/scramble-text.jsonThe file lands in components/goodui/scramble-text.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/scramble-text works too.
Usage
import { ScrambleText } from "@/components/goodui/scramble-text"
export function BookingConfirmed({ reference }: { reference: string }) {
return (
<div className="rounded-xl border bg-card p-6 text-center">
<p className="text-sm text-muted-foreground">Booking reference</p>
<ScrambleText text={reference} trigger="view" className="font-mono text-2xl font-semibold tracking-widest" />
</div>
)
}When to use
- Short technical labels that should feel computed: API key names, status codes, "Deploying…".
- Nav links or buttons that scramble once on hover for a developer-tool vibe (trigger="hover").
- Revealing a generated value (a confirmation code, a booking reference) after an action, with replay.
- Headlines and long sentences: random glyphs make them unreadable for most of the animation. Use text-reveal.
- Switching between two readable labels: use text-morph, which keeps shared letters in place.
- Numbers that change: use number-flow.
| If you need | Use |
|---|---|
| Play on page load | trigger="mount" (default) |
| Play when it scrolls into view | trigger="view" |
| Play on hover | trigger="hover" |
| Play from your code | trigger="manual" and change replay (e.g. a counter) |
| Numbers-only glyphs | characters="0123456789" |
Examples
Replay from a button
const [run, setRun] = React.useState(0)
<ScrambleText text="sk_live_••••4f2a" trigger="manual" replay={run} className="font-mono" />
<button onClick={() => setRun((n) => n + 1)}>Regenerate key</button>Hover scramble on a nav link
<a href="/docs" className="font-mono text-sm uppercase">
<ScrambleText text="Documentation" trigger="hover" duration={600} />
</a>API reference
| Prop | Type | Default |
|---|---|---|
| text* The final text; it is rendered as-is on the server and before the first run. | string | — |
| trigger When to play: on mount, when scrolled into view, on hover/focus (of the text or its closest link/button), or only via `replay`. | "mount" | "view" | "hover" | "manual" | "mount" |
| replay Change this value (e.g. a counter) to play again. | number | string | — |
| duration Milliseconds until the last character resolves. | number | 900 |
| speed Milliseconds between glyph changes. | number | 45 |
| characters Glyphs used while scrambling. | string | GLYPHS |
| scrambledClassName Classes for the not-yet-resolved part (default: 60% opacity of the current color). | string | — |
| onComplete Called when the last character has resolved. | () => void | — |
Also accepts every <span> attribute.
Accessibility
- The real text is always in an sr-only span; the scrambling copy is aria-hidden, so screen readers never read random glyphs.
- The real text is rendered on the server and before the first run; randomness only happens after mount, so there is no hydration mismatch.
- trigger="hover" also plays on keyboard focus: the span itself is not focusable, so it listens to its closest link, button or [tabindex] ancestor (focus and pointer enter). Put it inside a link or button for keyboard users to get the same effect.
Motion
- Characters resolve left to right over `duration` ms (900); the unresolved tail re-rolls random glyphs every `speed` ms (45). Spaces are kept.
- The unresolved part is the current color at 60% opacity, so it keeps contrast on muted or tinted backgrounds (override with scrambledClassName).
- Reduced motion: no scramble; the real text is shown and onComplete is not called.
- Uses requestAnimationFrame for at most `duration` ms per run and cancels on unmount; nothing runs between plays.
Notes for AI
Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.
- Use the text prop; children are not accepted.
- duration and speed are milliseconds.
- replay replays on every change of its value, whatever the trigger; the first value does not play.
- Pair it with a monospace font (font-mono) so the width does not jump while glyphs change.
FAQ
Why does the width jump while scrambling?
Random glyphs have different widths in proportional fonts. Use a monospace font, or restrict characters to glyphs of similar width.
Is the real text in the server HTML?
Yes. The server and the first client render show the real text; the scramble only starts after mount.
Related
Morphs between strings letter by letter: shared characters spring to their new positions while the rest blur in and out.
Types and deletes a list of phrases with a human rhythm and a caret that only blinks while idle; works as an animated input placeholder.
Reveals a heading word by word or letter by letter with a staggered blur, fade and rise when it scrolls into view or on demand.
Digits roll to the new value with locale formatting, prefixes and suffixes.