Split-text reveal
Split-text reveal is a text-animation pattern that breaks a headline or paragraph into characters, words, or lines with GSAP SplitText, then staggers each unit into view so the text assembles itself instead of just appearing. It's the standard way modern brand sites give typography a moment of motion on scroll or page load.
Updated June 30, 2026
How a split-text reveal works
There are two halves to it. First you split the text: SplitText.create() wraps every char, word, or line in its own element and hands you back .chars, .words, and .lines arrays. Then you animate those arrays with a stagger, so each unit starts a beat after the one before it. That offset is the whole effect.
The reveal reads best when the units rise from behind a clipped edge rather than just fading. SplitText has a mask option for exactly this: mask: 'lines' (or 'words' / 'chars') wraps each unit in an extra element with overflow: clip, so a yPercent: 100 start sits below the mask and slides up into frame. You get the wipe without hand-writing the wrapper markup. Source: the GSAP SplitText docs at gsap.com/docs/v3/Plugins/SplitText.
Build it clobber-proof
from parks the unit) and restart() it on trigger, rather than creating a fresh delayed tween at trigger time. In Annnimate this is what RevealHeadline and AnimatedText do internally.Chars, words, lines, and the mask wipe
The granularity you split at sets the whole feel of the reveal.
Characters
type: 'chars,words' gives the most granular reveal. Each letter staggers in, so a short headline assembles letter by letter. Keep the stagger tight (0.01 to 0.03) or a long line takes too long to finish. Split chars alongside words so wrapping still respects word boundaries.
Words
type: 'words' reveals each word as a unit. It reads more natural for longer headlines and subheads because the eye groups by word anyway. Lower unit count means you can run a slightly longer per-unit duration without the reveal dragging.
Lines
type: 'lines' wipes paragraph copy one line at a time, which reads as 'reading' the text. Line splits depend on the rendered width, so they break on resize and on late-loading fonts. Use autoSplit: true and create the animation inside onSplit() so SplitText re-splits and re-syncs when the layout settles.
The mask wipe
Add mask: 'lines' to any of the above and the units rise out from behind a clipped edge instead of fading in place. This is the editorial look most studio headlines use. The masked line/char reveal is the highest-impact version and the one worth reaching for first.
Use a split-text reveal for
- Hero headlines that need one deliberate moment of motion on load or scroll-enter
- Editorial line-by-line reveals on paragraph copy, testimonials, or pull quotes
- Letter-by-letter assembly on short, high-emphasis words (a logotype line, a section label)
- Any text where the typography itself is the thing you want the eye to land on
Use something else when
- The whole block can just fade or slide as one unit, then animate the element directly and skip the split entirely
- You want randomized character mutation rather than a positional reveal, that's a scramble effect (ScrambleText), not a stagger
- The text is a number counting up, use a numeric
gsap.to({ value })tween, not character splitting - Lighthouse or a screen reader needs the raw text intact at first paint, run the split client-side inside
useGSAPso the SSR HTML stays unwrapped
Used in these Annnimate components
Four library components are split-text reveals, each tuned for a different granularity and feel:
- Text Reveal splits a headline into words or chars and staggers a y/opacity rise on scroll-enter, the default hero-headline reveal
- Character Appear splits into chars and fades each one in with opacity only, sequential or random order, for a typewriter feel without movement
- Mask Reveal drives the wipe side of the pattern, revealing content from behind shape masks (rectangle, circle, blob, custom clip-paths) the way the SplitText
maskoption wipes each line - Rectangle Text Reveal splits paragraph copy into lines and slides each line in from behind a rounded rectangle that scales to zero, an editorial reveal for testimonials and longer copy
Always gate on reduced-motion
prefers-reduced-motion are asking to avoid. Adoption of that query jumped from 34% of mobile sites in 2022 to over 50% in 2024 (Web Almanac 2024, Accessibility), so this is a mainstream preference, not an edge case. Wrap the reveal in gsap.matchMedia() and drop straight to the final state (units at opacity 1, no stagger) when reduce is set.See it running in production
Common questions
- How do I make text reveal character by character in GSAP?
- Split the element with SplitText (
SplitText.create(el, { type: 'words,chars' })), then tween the returned.charsarray with a stagger:gsap.from(split.chars, { yPercent: 100, opacity: 0, stagger: 0.02, ease: 'expo.out' }). Keep the stagger between 0.01 and 0.03 so a long line doesn't take forever to finish. Callsplit.revert()on cleanup. - What's the difference between split-text reveal and a scramble effect?
- A split-text reveal staggers each unit into its final position (rise, fade, wipe). A scramble effect cycles each character through random glyphs before settling on the real letter, which is ScrambleText, a different plugin. Reveal is positional motion, scramble is character mutation. You can stack them, but they answer different briefs.
- Why does my line-by-line reveal break on resize or font load?
- Line splits are cached against the rendered width at split time. When the container resizes or a custom font loads and reflows the text, the line breaks change but the wrappers don't, so the animation targets stale lines. Fix it with
autoSplit: trueand create the animation insideonSplit(), which re-splits and re-syncs when the layout settles. Chars and words splits don't have this problem since they don't depend on layout. - How do I get the masked wipe where text slides up from behind an edge?
- Pass
mask: 'lines'(or'words'/'chars') toSplitText.create(). It wraps each unit in an extra element withoverflow: clip, so ayPercent: 100start position sits hidden below the mask and the unit slides up into view. No hand-written wrapper markup needed. This is the editorial reveal most studio headlines use. - Is SplitText free to use for split-text reveals?
- Yes. As of GSAP 3.13 (mid-2024) SplitText ships in the public GSAP package and is free for commercial use, including all the former Club GreenSock plugins. Install with
npm i gsapand import fromgsap/SplitText. The rewrite that landed with the free release is also about half the previous file size.
