Text Effects
StaggerType
Letters drop in from above and settle with a spring, timed outward from the start, centre or end of the line.
Download
- React
- Tailwind CSS v4
- motion
components/ui/stagger-type.tsx
"use client";
import { motion, useReducedMotion } from "motion/react";
type StaggerTypeProps = {
text: string;
className?: string;
/** Where the stagger starts: the first letter, the middle, or the last. */
from?: "start" | "center" | "end";
/** Seconds between neighbouring letters. */
stagger?: number;
/** Play only the first time it scrolls into view. */
once?: boolean;
};
/* StaggerType: letters drop in from above and settle with a spring, timed outward from the start, centre or end of the line. */
export function StaggerType({ text, className = "", from = "center", stagger = 0.03, once = true }: StaggerTypeProps) {
const reduce = useReducedMotion();
if (reduce) return <span className={className}>{text}</span>;
const words = text.split(" ");
const starts = words.map((_, w) => words.slice(0, w).join("").length);
const last = text.replace(/ /g, "").length - 1;
const origin = from === "start" ? 0 : from === "end" ? last : last / 2;
return (
<motion.span className={className} initial="hidden" whileInView="show" viewport={{ once, amount: 0.6 }}>
<span className="sr-only">{text}</span>
<span aria-hidden>
{words.map((word, w) => (
<span key={w}>
<span className="inline-block whitespace-nowrap">
{[...word].map((ch, i) => (
<motion.span
key={i}
className="inline-block"
custom={Math.abs(starts[w] + i - origin)}
variants={{
hidden: { opacity: 0, y: "-0.6em", scale: 1.4 },
show: (d: number) => ({
opacity: 1,
y: 0,
scale: 1,
transition: { type: "spring", stiffness: 300, damping: 20, delay: d * stagger },
}),
}}
>
{ch}
</motion.span>
))}
</span>
{w < words.length - 1 && " "}
</span>
))}
</span>
</motion.span>
);
}
1. Install the dependency.
npm i motion2. Paste the Code tab into
components/ui/stagger-type.tsx. The styles are Tailwind CSS v4 classes.3. Use it. This is the preview above:
app/stagger-type-demo.tsx
"use client"; import { useState } from "react"; import { StaggerType } from "@/components/ui/stagger-type"; export default function StaggerTypeExample() { const [run, setRun] = useState(0); return ( <div className="grid h-full place-items-center bg-[#fbbf24] p-6"> <div className="max-w-xl text-center"> <StaggerType key={run} text="Drop it like it lands" className="text-[clamp(30px,5.5vw,60px)] font-black tracking-tight text-[#121212]" /> <button type="button" onClick={() => setRun((r) => r + 1)} className="mt-6 block w-full text-xs text-[#121212]/60 hover:text-[#121212]"> Replay </button> </div> </div> ); }
Props
| Name | Type | Default | What it does |
|---|---|---|---|
| text | string | — | Text to show. |
| from | "start" | "center" | "end" | "center" | Where the stagger starts. |
| stagger | number | 0.03 | Seconds between neighbouring letters. |
| once | boolean | true | Play only the first time it scrolls into view. |
| className | string | "" | Type styles for the text. |