Text animations in React: which one to use
Pick a React text animation by its job: entrance, scroll reveal, CTA highlight, stats or decoding state, with screen reader text and reduced motion built in.
Published by Mekai Games
Pick a text animation by when it runs: on page load, when a section scrolls into view, when someone points at it, on a loop, or when a value changes. Pick the look second. Every example comes from the Text Animations category of Mekai UI: one client component per effect, built on Motion.
Match the effect to the job
- Entrance on load: Blur Text, Split Text and Masked Reveal for the hero headline.
- Reveal on scroll: the same three, which start when the text enters the viewport.
- Attention on a call to action: Shiny Text for a small label that loops, Stroke Text for links that fill on hover or focus, Echo Text for a large display headline.
- Numbers and stats: Number Ticker, part of Pro, counts to each new value.
- Loading and decoding: Typewriter for cycling status phrases, Scramble Text for labels that decode on hover, and the Pro Split Flap Text for a departure-board line.
- Decoration: Circular Text around a logo or badge, True Focus for a short sentence read one word at a time.
Blur Text
Text AnimationsSplit Text
Text AnimationsMasked Reveal
Text AnimationsShiny Text
Text AnimationsStroke Text
Text AnimationsTypewriter
Text AnimationsEntrance on load and on scroll
Blur Text, Split Text and Masked Reveal use Motion's useInView hook. They play once part of the element is visible (30, 40 and 35 percent), and once defaults to true, so they don't replay on scroll back. In a hero that is visible on arrival, they play on load. What differs is the unit that moves:
- Blur Text animates words. Each one rises out of a 12px blur,
staggerseconds after the one before (0.08 by default). A ten-word headline is fully sharp after about 1.5 seconds. - Split Text animates letters with a spring, 0.035 seconds apart. That adds up: the last letter of a 30-letter line starts about a second after the first. Use it for two to four short words.
- Masked Reveal animates lines. You pass
linesas an array, each line slides up from behind a mask, and by default an accent line draws in under the last one.
import { BlurText } from "@/components/ui/blur-text";
import { MaskedReveal } from "@/components/ui/masked-reveal";
import { SplitText } from "@/components/ui/split-text";
export default function HomePage() {
return (
<main className="mx-auto max-w-4xl px-6">
<section className="py-32">
<MaskedReveal
as="h1"
lines={["Build worlds", "Ship stories"]}
stagger={0.12}
className="text-6xl font-black tracking-tight"
/>
<p className="mt-8 max-w-xl text-xl text-neutral-400">
<BlurText text="Animated components for React and Tailwind." delay={0.6} stagger={0.05} />
</p>
</section>
<section className="py-24">
<h2 className="text-5xl font-black uppercase">
<SplitText text="Ready for launch" from="bottom" />
</h2>
<p className="mt-6 text-lg text-neutral-400">
This paragraph stays plain text. Animate the heading, not the copy under it.
</p>
</section>
</main>
);
}Attention on a call to action
These run on a loop or on interaction, so keep them to short labels, one per screen.
- Shiny Text sweeps a band of light across the text every
speedseconds (2.4 by default), with a short pause between sweeps. It suits a badge such as "Now in early access" above a headline. - Stroke Text shows outlined text that fills with color on hover or focus, sweeping in from the left, the bottom or the center.
activekeeps it filled, which marks the current page in a menu. - Echo Text stacks outlined or solid copies behind a headline. They breathe in a slow loop and swing toward the pointer on hover, so save it for large display type rather than buttons.
- In Pro, Gradient Text and Glitch Text cover the same job with a flowing gradient fill and RGB-split bursts.
Stroke Text is a tab stop by default, so keyboard users can trigger the fill. Inside a link, set focusable={false} to avoid a second tab stop, and give the link its own focus style: the fill responds to hover and focus on the text itself, not on the parent link.
import Link from "next/link";
import { StrokeText } from "@/components/ui/stroke-text";
const LINKS = [
{ href: "/", label: "Home" },
{ href: "/games", label: "Games" },
{ href: "/press", label: "Press" },
];
export function SiteNav({ current }: { current: string }) {
return (
<nav className="flex gap-8 text-3xl font-black uppercase">
{LINKS.map((link) => (
<Link
key={link.href}
href={link.href}
aria-current={link.href === current ? "page" : undefined}
className="rounded-md focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-orange-400"
>
<StrokeText text={link.label} direction="bottom" focusable={false} active={link.href === current} />
</Link>
))}
</nav>
);
}Numbers, stats and decoding states
For a figure that changes, such as downloads or a score, Number Ticker counts from its previous value to the new one, and from zero on first render. It sets tabular-nums so digits keep their width while counting. Its format prop is a function, which a Server Component can't pass, so render it inside a client component that owns the value.
Decoding effects fit labels and status text:
- Scramble Text decodes from random glyphs, left to right, over
durationmilliseconds (700 by default). It runs when the pointer enters or the element receives focus, not on mount, so it suits nav links, codenames and buttons rather than loading screens. - Typewriter types each of its
phrases, deletes it and moves to the next, with a caret that blinks while it waits. Setloop={false}to stop on the last phrase, for example a boot sequence that ends on "Ready". - Split Flap Text (Pro) flips every cell of a departure-board line through the alphabet to reach the next message. Rotating Text (Pro) swaps one word inside a headline, in a pill that resizes to fit.
None of these components uses an aria-live region, so they don't announce changes. If the text reports real progress, such as an upload, put the status in your own live region and treat the animation as decoration.
Accessibility: screen readers and reduced motion
Splitting text into letters or words breaks it up for assistive technology unless the component handles it. Here is what the free components do:
- Blur Text, Split Text, Masked Reveal, Typewriter and Circular Text put the full text in a visually hidden
sr-onlyspan and mark the animated copyaria-hidden. Masked Reveal joins its lines with spaces, Typewriter lists all its phrases separated by commas, and Circular Text gives the text once even whenrepeatdraws it twice around the ring. - Scramble Text puts the final text in
aria-labeland hides the changing glyphs witharia-hidden. It is also a tab stop, so keyboard users can trigger the decode. - Shiny Text, Echo Text, Stroke Text and True Focus keep the real text as normal text. The extra layers (echo copies, the fill layer, the focus frame) are
aria-hidden. - True Focus makes every word a tab stop so keyboard users can move the focus. A five-word sentence adds five stops, one more reason to keep it short.
Every component also reads prefers-reduced-motion through Motion's useReducedMotion hook.Blur Text, Split Text and Masked Reveal render their final state at once. Shiny Text renders plain text in its base color, Circular Text keeps the ring still and Echo Text holds its copies in place. Typewriter shows one complete phrase without typing: the first when it loops, the last when it doesn't. Scramble Text skips the scramble, True Focus shows every word sharp and stops its timer, and Stroke Text fills instantly.
Don't animate long paragraphs. Split Text creates one animated element per letter, so 400 letters means 400 separate animations. Blur Text needs about five and a half seconds to settle 60 words at the default stagger, and anyone who starts reading early reads blurred text. Animate the heading and leave the copy plain. For a short statement that should light up as the reader moves, Scroll Reveal Text (Pro) ties each word's brightness to scroll position instead of a timer, so the reader sets the pace.
Install and use from server pages
npx shadcn@latest add https://ui.mekaigames.com/r/blur-text.json https://ui.mekaigames.com/r/masked-reveal.jsonEach free component installs with the shadcn CLI and lands in components/ui. Pro components come from a private GitHub repository after purchase. Installation covers both.
Every file in the category starts with "use client", so the page in the first example stays a Server Component and passes strings, numbers, booleans and arrays directly. Write your own client wrapper only when a prop is a function, like Number Ticker's format, or when the text comes from client state.
Components in this guide
More guides
- Animated hero backgrounds in Next.jsAdd an animated background behind a Next.js App Router hero: choose DOM, canvas or WebGL, stack content on top, keep text readable and spare the battery.
- Gamification UI in React: XP, streaks, leaderboardsBuild XP bars, streaks, daily rewards, achievements, leaderboards and loot reveals in React, and learn when each one helps users and when it turns into noise.
- Respecting prefers-reduced-motion in ReactRead prefers-reduced-motion in React with Motion or CSS, decide what reduced should mean, pause canvas and WebGL loops, and test the setting in Chrome DevTools.
- Install React components from a shadcn registryHow the shadcn CLI installs components from a custom registry URL, what lands in your project, and how to add, update and edit Mekai UI components.