Word rotate
Free · MITSwaps 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.
Installation
$ npx shadcn@latest add https://goodui.dev/r/word-rotate.jsonThe file lands in components/goodui/word-rotate.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/word-rotate works too.
Usage
import { WordRotate } from "@/components/goodui/word-rotate"
export function Hero() {
return (
<h1 className="text-4xl font-semibold tracking-tight">
Online booking for{" "}
<WordRotate words={["cleaners", "hair salons", "dog groomers", "tutors"]} className="text-primary" />
</h1>
)
}When to use
- Hero headlines that cycle the audience or use case ("Scheduling for cleaners / salons / tutors").
- Short inline swaps inside a sentence, where the rest of the sentence should glide rather than jump.
- Feature carousels where the highlighted word should follow your own index (controlled mode).
- Showing whole sentences one after another: use typewriter or text-reveal.
- Changing a value people read (status, count): use text-morph for labels or number-flow for numbers.
- Key information such as a price or plan name: put it in static text (text or heading); a rotating word is easy to miss.
| If you need | Use |
|---|---|
| Auto-rotate in a headline | words={[…]} (interval 2400 ms) |
| Synced to your own carousel or tabs | index={i} onIndexChange={setI} |
| Centered headline | align="center" |
| Stop rotating (e.g. on hover) | paused |
Examples
Controlled, synced with a feature list
const [i, setI] = React.useState(0)
<h2 className="text-3xl font-semibold">
<WordRotate words={["Bookings", "Invoices", "Reminders"]} index={i} onIndexChange={setI} align="center" />
{" "}on autopilot
</h2>API reference
| Prop | Type | Default |
|---|---|---|
| words* Words or short phrases shown one at a time, in order. | string[] | — |
| interval Milliseconds each word stays before the next one. | number | 2400 |
| index Controlled index of the visible word. | number | — |
| defaultIndex Starting word when uncontrolled. | number | 0 |
| onIndexChange Called with the next index on every automatic rotation. | (index: number) => void | — |
| paused Stop auto-rotating (controls and `index` still work). | boolean | false |
| align Where the word sits while the box resizes. | "start" | "center" | "end" | "start" |
Also accepts every <span> attribute.
Accessibility
- An sr-only span holds the current word; the animated copies are aria-hidden.
- It is not a live region, so screen readers are not interrupted on every rotation.
Motion
- The outgoing word slides up 0.7em and blurs while the next slides in from below, on a spring (stiffness 300, damping 30, mass 0.7).
- The box width springs to the incoming word (stiffness 260, damping 30, mass 0.8), so the words around it glide; the very first measurement is applied without animation.
- Rotation waits `interval` ms (2400) per word and pauses while off-screen, while the tab is hidden and when paused is set.
- Reduced motion: words cross-fade in 0.15 s and the width changes instantly.
Notes for AI
Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.
- words is required and needs at least two entries to rotate.
- interval is milliseconds.
- In controlled mode, update index from onIndexChange, or rotation stops at the same word.
- It renders an inline-block <span>; put it inside your heading and style color and weight with className.
- Keep the words similar in length when possible, so the sentence does not reflow heavily.
FAQ
Why does the sentence not jump when the word changes?
The word sits in a box whose width springs to the next word's measured width, so neighbouring words move smoothly. It re-measures when fonts load.
Can I drive it from buttons?
Yes, pass index (and onIndexChange); setting paused stops only the automatic rotation.
Related
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.
Morphs between strings letter by letter: shared characters spring to their new positions while the rest blur in and out.
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.
Layered heading: semantic level and size decoupled, sans/serif/mono/display font slots you map to your own fonts, eight ink treatments (gradient, muted tail, outline, marker highlight, hand-drawn squiggle, shimmer…), serif-italic emphasis via _words_, and on-view word, letter and mask reveals.