Text morph
Free · MITMorphs between strings letter by letter: shared characters spring to their new positions while the rest blur in and out.
Installation
$ npx shadcn@latest add https://goodui.dev/r/text-morph.jsonThe file lands in components/goodui/text-morph.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/text-morph works too.
Usage
import * as React from "react"
import { TextMorph } from "@/components/goodui/text-morph"
export function FollowButton() {
const [following, setFollowing] = React.useState(false)
return (
<button
type="button"
onClick={() => setFollowing((f) => !f)}
className="inline-flex h-9 items-center rounded-lg border px-4 text-sm font-medium"
>
<TextMorph text={following ? "Following" : "Follow"} />
</button>
)
}When to use
- Button and status labels that change in place: "Save" → "Saving" → "Saved", "Follow" → "Following".
- Toggle labels such as "Monthly" / "Yearly" on a pricing switch.
- Short headings that swap with app state ("3 bookings today" → "4 bookings today").
- Changing numbers that should roll like an odometer: use number-flow.
- Rotating through a list of words on a timer: use word-rotate.
- A button that needs full loading, success and error states: use stateful-button.
Examples
Billing period toggle label
<TextMorph text={yearly ? "Billed yearly · save 20%" : "Billed monthly"} className="text-sm text-muted-foreground" />API reference
| Prop | Type | Default |
|---|---|---|
| text* The current string; change it to morph. Characters both strings share slide into place. | string | — |
Also accepts every <span> attribute.
Accessibility
- An sr-only span holds the current string; the animated letters are aria-hidden.
- Inside a button, the button's accessible name follows the current text. It is not a live region; add one yourself if a status change must be announced.
Motion
- Each character is keyed by itself and its occurrence, so letters both strings share slide to their new position with a layout spring (stiffness 420, damping 34, mass 0.6).
- Letters that leave fade out, blur 4px and shrink to 0.9; new letters do the reverse, over 0.22 s.
- Reduced motion: letters swap instantly, with no slide, blur or fade.
Notes for AI
Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.
- The only prop besides HTML attributes is text (a string); children are not supported.
- Keep strings short (labels, not paragraphs): every character is its own animated span.
- It renders an inline-flex span with white-space: pre, so it never wraps; do not use it for multi-line text.
- Matching is case-sensitive: "Save" and "save" share only "ave".
FAQ
Why do some letters slide and others fade?
Characters present in both strings keep their identity and slide; characters that exist in only one string fade and blur in or out.
Related
Digits roll to the new value with locale formatting, prefixes and suffixes.
Swaps words in place with a vertical slide and blur while the box springs to each word's width, so the sentence glides instead of jumping.
Async button that morphs idle to spinner to drawn check or shaking error, with a spring width change between labels.
Text 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.