Text
Flip Words
A word carousel that rolls through a list without shifting the line around it.
Build interfaces that feel effortlesseffortless
import { FlipWords } from "@/components/ui/flip-words";
export default function FlipWordsDemo() {
return (
<p className="text-2xl font-medium tracking-tight sm:text-3xl">
Build interfaces that feel{" "}
<FlipWords
words={["effortless", "expensive", "alive", "yours"]}
className="text-accent"
/>
</p>
);
}Installation
1. Install the dependencies.
terminal npm install motion clsx tailwind-merge2. Add the
cnhelper, if you don't already have it.lib/utils.ts import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }3. Copy the component into your project.
components/ui/flip-words.tsx "use client"; import { AnimatePresence, motion } from "motion/react"; import { useEffect, useState } from "react"; import { cn } from "@/lib/utils"; type FlipWordsProps = { words: string[]; className?: string; /** Milliseconds each word stays on screen. */ interval?: number; }; export function FlipWords({ words, className, interval = 2200 }: FlipWordsProps) { const [index, setIndex] = useState(0); useEffect(() => { const id = window.setInterval( () => setIndex((current) => (current + 1) % words.length), interval, ); return () => window.clearInterval(id); }, [interval, words.length]); const word = words[index] ?? ""; return ( <span className="relative inline-flex overflow-hidden align-bottom"> {/* Reserves the width of the longest word so surrounding text never jumps. */} <span aria-hidden className={cn("invisible whitespace-pre", className)}> {words.reduce((a, b) => (b.length > a.length ? b : a), "")} </span> <AnimatePresence mode="wait"> <motion.span key={word} initial={{ y: "100%", opacity: 0 }} animate={{ y: 0, opacity: 1 }} exit={{ y: "-100%", opacity: 0 }} transition={{ duration: 0.4, ease: [0.22, 1, 0.36, 1] }} className={cn("absolute inset-0 whitespace-pre", className)} > {word} </motion.span> </AnimatePresence> </span> ); }
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| words | string[] | — | Words to cycle through, in order. |
| interval | number | 2200 | Milliseconds each word stays on screen. |
| className | string | — | Applied to the visible word and its width placeholder. |