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 splitoptions- 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, oTry 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 elementsTry 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 wrappersTry it: Nested Split
const result = TextSplitter.split('#text', { type: 'chars,words' });
// Characters animate individually, but words stay togetherOptions
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 spacingpunctuation- 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-labelon parent with original text - Sets
aria-hidden="true"on split elements
reducedMotion
Type: boolean | 'auto'
Default: 'auto'
Handle reduced motion preference:
'auto'- Respectsprefers-reduced-motionsystem settingtrue- Always mark as reduced motionfalse- 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 modefalse- 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 unitsTry it: Emoji Support
const result = TextSplitter.split('#text', {
type: 'chars',
preserve: ['emojis']
});Accessibility
TextSplitter is accessibility-first:
- Original text preserved:
aria-labelcontains original text - Split elements hidden:
aria-hidden="true"on spans - Reduced motion: Respects
prefers-reduced-motion - RTL support: Auto-detects text direction
- Live regions: Optional
aria-livefor 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
- Character Split - Basic character splitting
- Word Split - Word-level animation
- Line Split - Line-by-line reveal
- Sentence Split - Sentence-level animation
- Nested Split - Characters inside word wrappers
- Stagger Reveal - Staggered character reveal
- Emoji Support - Unicode and emoji handling