Rotating Text
A free rotating text effect that cycles one word of a headline with React and Motion.
Template Empire
Build faster with your team
One shared workspace for plans, reviews and releases, so the next step is always obvious. Teams at 4,000 companies run their week here instead of in status meetings.
Customise
Code
const words = ["faster", "smarter", "together", "calmer"];
<TextRotate words={words} prefix="Build " suffix=" with your team" />Rotating Text is a free React rotating text animation component for Next.js, built with Motion and styled with Tailwind. One word in your headline cycles on a timer: the outgoing word exits upward through a clipped slot while the incoming word rises in from below, each holding for 2.5 seconds by default.
The slot is only ever as wide as the word it is showing: an invisible sizer renders the current word, the slot keeps room for the outgoing word until its exit completes so a longer word is never clipped mid-exit, and then the slot and each word of the text after it glide to their new positions with Motion layout animations on the same curve as the swap. The sentence always reads with natural spacing, wraps exactly like static text on narrow screens (a suffix that starts with punctuation, like an exclamation mark or a unit, stays glued to its word), and keeps cumulative layout shift at zero. Give it a prefix or suffix and the heading also reserves its tallest layout up front, so a longer word that would push the sentence onto another line never changes the heading's height mid-cycle; drop it bare into your own sentence and it stays a plain inline box exactly as wide as the word on show, so the words around it share its line. The server HTML carries the complete sentence with the first word, the loop pauses while the headline is offscreen, screen readers hear one stable string from a screen-reader-only copy of the static sentence, and with prefers-reduced-motion enabled the first word renders statically at its own width with no timer and no movement.
Install
Add the @te registry to your components.json once:
{
"registries": {
"@te": "https://templateempire.io/r/{name}.json"
}
}Then install the component:
npx shadcn@latest add @te/text-rotateUsage
import { TextRotate } from "@/components/text-rotate";
export function Example() {
return (
<h1 className="text-5xl font-semibold tracking-tight">
<TextRotate
prefix="Build "
suffix=" with your team"
words={["faster", "smarter", "together"]}
/>
</h1>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| words | string[] | required | The words that cycle through the slot, 2 to 6 of them. The first is the SSR/static word. |
| prefix | string | undefined | Text rendered before the rotating slot. |
| suffix | string | undefined | Text rendered after the rotating slot. |
| interval | number | 2.5 | Seconds each word holds before cycling to the next. |
| className | string | undefined | Additional classes merged onto the wrapper. |
Customisation
Rotating Text declares no tokens of its own; the swap timing mirrors the --dur-2 and --ease-out-expo motion tokens through the shared lib/motion.ts presets, and the type inherits whatever heading styles surround it. Tune the pace with the interval prop and restyle the rotating slot from the className prop with the same semantic tokens the rest of the library uses:
<h1 className="text-5xl font-semibold">
<TextRotate
interval={4}
className="text-brand"
prefix="Ship "
suffix=" today"
words={["templates", "sections", "pages"]}
/>
</h1>Frequently asked questions
- Is Rotating Text free for commercial use?
- Yes. Rotating Text is MIT licensed, so you can use it in personal and commercial projects without attribution.
- Does Rotating Text cause layout shift or hurt accessibility?
- No. The slot sizes itself to the word on show, holds enough width for the outgoing word until its exit finishes, and when words swap the slot and each word after it move with Motion layout animations: FLIP transforms applied in the same frame as the layout change, so the whole swap animates transform and opacity only and cumulative layout shift stays at zero. When you pass a prefix or suffix the wrapper also stacks an invisible copy of the sentence per word, so the heading holds its tallest line count from the start and content below it never moves; used bare inside your own sentence it stays an inline box the width of the current word, and how the surrounding line re-wraps as widths change is down to your composition. It exposes one stable screen-reader-only string with the static sentence while the rotating machinery stays hidden from assistive technology, and with prefers-reduced-motion enabled the first word renders statically at its own width with no timer at all.
- How do I change the words, the pace or the styling?
- Pass 2 to 6 strings through the words prop; the first is the word crawlers and no-JS visitors see. Set the interval prop to the number of seconds each word should hold, 2.5 by default, and merge your own Tailwind classes onto the wrapper through the className prop to control size, weight and colour.