Text Splitter

Split text into chars, words, or lines for per-element animation.

Text Splitter breaks a text element into individual <span> nodes — one per character, word, or line — so each unit can animate independently. Pair it with Stagger to create cascading text reveals: words sliding in one by one, characters popping in sequence, lines rising from below a clip boundary.

Split each character, word or line into its div, and animate them one by one using Stagger.

Location

Left Panel → Animation tab → Functional Properties → Text Splitter

Text Splitter is a functional property — it is direction-agnostic. The same split applies whether the From, To, or Set tab is active. Enabling it on any tab automatically enables it on all three.

Enable Text Splitter

Click the toggle next to Text Splitter to enable it. The row expands to reveal the type dropdown, class input, Reduce White Space toggle, and Mask toggle. A live preview fires immediately in the builder so you can see the split in action.

Text Splitter controls expanded — type dropdown, class input, and toggles

Controls

ControlTypeDefaultDescription
TypeDropdownCharactersThe unit each piece of text is split into — Characters, Words, or Lines.
ClassText input(empty)Optional CSS class applied to every split span. Dot prefixes are stripped automatically. Append ++ to add incrementing numbers: .char1, .char2, .char3.
Reduce White SpaceToggleOnCollapses extra whitespace between words for cleaner splitting. Leave on unless your layout depends on multiple spaces.
MaskToggleOffWraps each split element in an overflow: hidden container for cinematic clip-reveal effects (e.g. text sliding up from behind an invisible edge).

Split type options

ValueDescription
CharactersEvery character — including spaces — becomes its own animatable span. Use tight stagger values (0.020.05).
WordsEach word becomes a span. Use looser stagger values (0.060.1).
LinesEach line of text (based on the element’s actual word-wrap) becomes a span. Typically needs stagger values of 0.10.2.

Note on Lines: Line splitting reflects the element’s actual layout at animation time. If the container resizes, the line count changes. Test at your target breakpoint.

Mask mode

Enable Mask to wrap each split unit in an overflow: hidden container. Animate with y: "110%" on the From tab to slide each unit up from behind the invisible clip boundary — a clean newspaper-headline reveal with no extra CSS required.

The mask containers are automatically unstyled beyond overflow: hidden so they do not affect spacing or layout. Combine with any From translate offset for the reveal direction: y: "110%" for up, x: "-110%" for from the left.

Pairing with Stagger

Text Splitter works alongside Stagger, which appears directly below it in the Functional Properties list. Stagger controls the cascade — the delay between each character, word, or line entering. Without stagger, all split units animate simultaneously, which defeats the purpose.

Enable both Text Splitter and Stagger together. A typical setup:

  • Type: Words
  • Mask: On
  • Stagger Each: 0.08 s, From: start
  • From tab: y: 110%, opacity: 0

This produces each word sliding up from behind its clip boundary, left to right.

Text Splitter also re-enables Stagger when using the Interactions trigger in Click/Hover Multiple mode — normally stagger is disabled in that mode, but adding Text Splitter lifts that restriction.

Each character starts at y: 110% behind its mask container and slides up into place, left to right. This is Text Splitter set to Characters, Mask on, combined with Stagger Each: 0.04.

SDK equivalent

Text Splitter maps to the split and mask properties in the SDK config. The builder only supports single-level splits (chars, words, lines) — combined values like 'chars,words' are available in the SDK directly.

Characters with mask (From mode):

typescript
import { Motion } from '@motion.page/sdk';

Motion('headline', 'h1', {
  split: 'chars',
  mask: true,
  from: { y: '110%', opacity: 0 },
  duration: 0.48,
  stagger: { each: 0.04, from: 'start' },
  ease: 'power3.out',
}).onPageLoad();

Words slide in on scroll:

typescript
Motion('section-title', '.section-title', {
  split: 'words',
  mask: true,
  from: { y: '110%' },
  duration: 0.6,
  stagger: { each: 0.08, from: 'start' },
  ease: 'power3.out',
}).onScroll({
  each: true,
  toggleActions: 'play none none none',
  start: 'top 85%',
});

Lines fade up:

typescript
Motion('paragraph', 'p', {
  split: 'lines',
  from: { opacity: 0, y: 40 },
  duration: 0.6,
  stagger: { each: 0.15, from: 'start' },
  ease: 'power2.out',
}).onPageLoad();

For the full SDK reference, see Text Splitter in the SDK.

Common patterns

Words slide up from mask

Enable Text SplitterWords, turn on Mask. On the From tab set y to 110%. Add StaggerEach: 0.08, From: start.

typescript
Motion('words-mask', 'h2', {
  split: 'words',
  mask: true,
  from: { y: '110%' },
  duration: 0.6,
  stagger: { each: 0.08, from: 'start' },
  ease: 'power3.out',
}).onPageLoad();

Characters fade in randomly

Set Type to Characters, leave Mask off. On From, set opacity: 0 and y: 20. Set StaggerEach: 0.02, From: random for an organic, shuffled entrance.

typescript
Motion('chars-random', '.display-title', {
  split: 'chars',
  from: { opacity: 0, y: 20 },
  duration: 0.4,
  stagger: { each: 0.02, from: 'random' },
  ease: 'power2.out',
}).onPageLoad();

Typewriter effect

Set Type to Characters. On the From tab, set only opacity: 0. Keep Duration very short (0.05 s) or set it to 0. Set StaggerEach: 0.07, From: start. The characters snap in one by one at a typed cadence.

typescript
Motion('typewriter', '.terminal', {
  split: 'chars',
  from: { opacity: 0 },
  duration: 0,
  stagger: { each: 0.07, from: 'start' },
}).onPageLoad();

Lines rise on scroll

Set Type to Lines. On From, set opacity: 0 and y: 40. Set StaggerEach: 0.15, From: start. Use a Scroll trigger so each paragraph line enters as it scrolls into view.

typescript
Motion('lines-scroll', '.body-text', {
  split: 'lines',
  from: { opacity: 0, y: 40 },
  duration: 0.6,
  stagger: { each: 0.15, from: 'start' },
  ease: 'power2.out',
}).onScroll({
  each: true,
  toggleActions: 'play none none none',
  start: 'top 80%',
});

Custom class with numbered spans

Enter a class name in the Class field — for example word. Each span receives the class word. Append ++ to the name (word++) to produce word1, word2, word3, and so on — useful for targeting individual splits in CSS.

css
/* Target spans with the numbered class pattern */
.word1 { color: #6633EE; }
.word2 { color: #aa88ff; }

Tips

  • Apply Text Splitter to text elements onlyh1h6, p, span, li, a, and similar inline/block text containers. Applying it to layout wrappers (e.g. section, div with mixed content) may produce unexpected results.
  • Stagger is required for visible sequencing. Without Stagger, every split unit animates at exactly the same time.
  • Lines are layout-sensitive. If the window resizes between page load and animation, line boundaries may differ from when the split was computed. Use the SDK’s onResize option to recompute on resize when line splitting is critical.
  • Mask + y: "110%" is the gold standard for text reveal. The 110% ensures the starting position fully clears the clip boundary even with sub-pixel rounding.
  • Text Reveals presets in the Animation Presets panel include 20 ready-made Text Splitter animations — a fast starting point for characters, words, and lines.
  • Stagger — Control the cascade delay between split units
  • Animation Presets — 20 ready-made Text Reveals presets using Text Splitter
  • From, To & Set — How functional properties apply across animation mode tabs
  • Translate — Set y offset values used in Text Splitter reveals
  • Opacity — Often combined with Text Splitter for fade reveals
  • Text Splitter in the SDK — Full SDK reference including combined split values