FasterMotion
API ReferenceText

TextSplitter

Split DOM text into animatable characters, words, lines, or sentences

TextSplitter intelligently splits text content in DOM elements into individual animatable units. It handles Unicode, emojis, ligatures, RTL text, and accessibility automatically.

Basic Usage

import { TextSplitter, Timeline } from 'faster-motion';

// Split heading text into characters
const result = TextSplitter.split('#heading', { type: 'chars' });

// Animate each character with stagger (durations in seconds)
const tl = new Timeline();
result.elements.forEach((el, i) => {
  tl.fromTo(el,
    { opacity: 0, y: 30 },
    { opacity: 1, y: 0 },
    0.5,      // duration: 0.5 seconds
    i * 0.05  // stagger: 50ms each
  );
});
tl.play();

Try it: Character Split

const result = TextSplitter.split('#heading', { type: 'chars' });
result.elements.forEach((el, i) => {
  tl.fromTo(el, { opacity: 0, y: 30 }, { opacity: 1, y: 0 }, 0.5, i * 0.05);
});

TextSplitter.split()

The main method for splitting text content.

Syntax

TextSplitter.split(
  target: string | HTMLElement | NodeListOf<HTMLElement>,
  options?: SplitOptions
): SplitResult | SplitResult[]

Parameters

  • target - CSS selector, HTMLElement, or NodeList of elements to split
  • options - Configuration object (see options below)

Returns

SplitResult object (or array if multiple elements):

interface SplitResult {
  elements: HTMLElement[];  // Array of created span elements
  original: string;         // Original text content
  type: SplitType;          // Split type used
  count: number;            // Number of elements created
  parent: HTMLElement;      // Parent element that was split
}

Split Types

Characters

Split text into individual characters.

const result = TextSplitter.split('#text', { type: 'chars' });
// "Hello" โ†’ 5 elements: H, e, l, l, o

Try it: Split Characters

const result = TextSplitter.split('#text', { type: 'chars' });
console.log(`Split into ${result.count} characters`);

Words

Split text into words, preserving spaces.

const result = TextSplitter.split('#text', { type: 'words' });
// "Hello World" โ†’ 2 elements: "Hello", "World"

Try it: Split Words

const result = TextSplitter.split('#text', { type: 'words' });
console.log(`Split into ${result.count} words`);

Lines

Split text by line breaks (explicit \n or visual wrapping).

const result = TextSplitter.split('#text', { type: 'lines' });

Try it: Split Lines

const result = TextSplitter.split('#text', { type: 'lines' });
console.log(`Split into ${result.count} lines`);

Sentences

Split text by sentence boundaries (., !, ?).

const result = TextSplitter.split('#text', { type: 'sentences' });
// "Hello. World!" โ†’ 2 elements

Try it: Split Sentences

const result = TextSplitter.split('#text', { type: 'sentences' });
console.log(`Split into ${result.count} sentences`);

Characters and Words (Nested)

Split into words containing characters - words wrap together but characters animate individually.

const result = TextSplitter.split('#text', { type: 'chars,words' });
// Returns character elements nested inside word wrappers

Try it: Nested Split

const result = TextSplitter.split('#text', { type: 'chars,words' });
// Characters animate individually, but words stay together

Options

type

Type: 'chars' | 'words' | 'lines' | 'sentences' | 'chars,words'

Default: 'chars'

How to split the text.

className

Type: string

Default: 'ft-split'

Base class name for created elements. Elements get classes like ft-split-char, ft-split-word, etc.

TextSplitter.split('#text', {
  type: 'chars',
  className: 'my-text'
});
// Creates: <span class="my-text my-text-char my-text-char-0">H</span>

tag

Type: string

Default: 'span'

HTML tag to use for wrapper elements.

preserve

Type: ('emojis' | 'ligatures' | 'spacing' | 'punctuation')[]

Default: ['emojis', 'spacing']

What to preserve when splitting:

  • emojis - Keep emoji sequences together (e.g., ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ)
  • ligatures - Preserve typographic ligatures (ff, fi, fl)
  • spacing - Maintain proper spacing
  • punctuation - Keep punctuation with adjacent words
TextSplitter.split('#text', {
  type: 'chars',
  preserve: ['emojis', 'ligatures', 'spacing']
});

aria

Type: boolean

Default: true

Add accessibility attributes. When enabled:

  • Sets aria-label on parent with original text
  • Sets aria-hidden="true" on split elements

reducedMotion

Type: boolean | 'auto'

Default: 'auto'

Handle reduced motion preference:

  • 'auto' - Respects prefers-reduced-motion system setting
  • true - Always mark as reduced motion
  • false - Ignore reduced motion preference

rtl

Type: boolean | 'auto'

Default: 'auto'

Handle right-to-left text:

  • 'auto' - Auto-detect RTL text (Hebrew, Arabic, etc.)
  • true - Force RTL mode
  • false - Force LTR mode

Animation Patterns

Staggered Reveal

const result = TextSplitter.split('#heading', { type: 'chars' });
const tl = new Timeline();

result.elements.forEach((el, i) => {
  tl.fromTo(el,
    { opacity: 0, y: 30 },
    { opacity: 1, y: 0 },
    0.5,      // duration: 0.5 seconds
    i * 0.05  // stagger: 50ms each
  );
});

tl.play();

Wave Animation

const result = TextSplitter.split('#text', { type: 'chars' });

function animateWave() {
  let frame = 0;

  function update() {
    result.elements.forEach((el, i) => {
      const offset = Math.sin((frame + i * 20) * 0.05) * 15;
      el.style.transform = `translateY(${offset}px)`;
    });
    frame++;
    requestAnimationFrame(update);
  }

  update();
}

Word-by-Word Reveal

const result = TextSplitter.split('#paragraph', { type: 'words' });
const tl = new Timeline();

result.elements.forEach((el, i) => {
  el.style.transformOrigin = 'center bottom';
  tl.fromTo(el,
    { opacity: 0, y: 40, rotationX: -90 },
    { opacity: 1, y: 0, rotationX: 0 },
    0.6,      // duration: 0.6 seconds
    i * 0.15  // stagger: 150ms each
  );
});

tl.play();

Utility Methods

TextSplitter.restore()

Restore element to original text content.

TextSplitter.restore(element);

TextSplitter.restoreAll()

Restore all split elements on the page.

TextSplitter.restoreAll();
// Or with selector
TextSplitter.restoreAll('.my-text');

TextSplitter.shouldReduceMotion()

Check if user prefers reduced motion.

if (TextSplitter.shouldReduceMotion()) {
  // Skip or simplify animations
}

TextSplitter.getSelector()

Get CSS selector for split elements.

const selector = TextSplitter.getSelector('chars', 'my-text');
// Returns: '.my-text-char'

TextSplitter.setInitialState()

Set initial CSS state for split elements before animating.

TextSplitter.setInitialState(result, {
  opacity: 0,
  transform: 'translateY(20px)'
});

Unicode & Emoji Support

TextSplitter uses Intl.Segmenter (with fallback) to properly handle:

  • Emojis: ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ stays as one unit
  • Compound emojis: ๐Ÿณ๏ธโ€๐ŸŒˆ preserved correctly
  • Skin tone modifiers: ๐Ÿ‘‹๐Ÿฝ kept together
  • Special characters: Accented characters, symbols
  • RTL scripts: Hebrew, Arabic with proper direction
TextSplitter.split('#emoji-text', {
  type: 'chars',
  preserve: ['emojis']
});
// "Hello ๐Ÿ‘‹ World ๐ŸŒ" โ†’ properly splits emojis as single units

Try it: Emoji Support

const result = TextSplitter.split('#text', {
  type: 'chars',
  preserve: ['emojis']
});

Accessibility

TextSplitter is accessibility-first:

  1. Original text preserved: aria-label contains original text
  2. Split elements hidden: aria-hidden="true" on spans
  3. Reduced motion: Respects prefers-reduced-motion
  4. RTL support: Auto-detects text direction
  5. Live regions: Optional aria-live for dynamic content
TextSplitter.split('#heading', {
  type: 'chars',
  aria: true,           // Enable accessibility (default)
  reducedMotion: 'auto', // Respect system preference
  rtl: 'auto',          // Auto-detect direction
  liveRegion: 'polite'  // For dynamic updates
});

Examples

See Also

On this page