Word rotate

Free · MIT

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.

MarkdownOpen in v0
Open in v0

Installation

$ npx shadcn@latest add https://goodui.dev/r/word-rotate.json

The 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

Use it for
  • 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).
Reach for something else
  • 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 needUse
Auto-rotate in a headlinewords={[…]} (interval 2400 ms)
Synced to your own carousel or tabsindex={i} onIndexChange={setI}
Centered headlinealign="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

PropTypeDefault
words*
Words or short phrases shown one at a time, in order.
string[]—
interval
Milliseconds each word stays before the next one.
number2400
index
Controlled index of the visible word.
number—
defaultIndex
Starting word when uncontrolled.
number0
onIndexChange
Called with the next index on every automatic rotation.
(index: number) => void—
paused
Stop auto-rotating (controls and `index` still work).
booleanfalse
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