This guide will help you create your first text animation with Griffo. Choose the approach that matches your project.
Choose Your Path
Vanilla JavaScript
React
Motion (Declarative)
Import splitText
Import the core splitText function from Griffo: import { splitText } from "griffo" ;
import { animate , stagger } from "motion" ;
Wait for fonts to load
Wrap your code in document.fonts.ready to ensure accurate measurements: document . fonts . ready . then (() => {
// Split and animate here
});
This ensures kerning measurements are accurate even with custom fonts
Split the text
Select an element and split it into characters, words, or lines: const element = document . querySelector ( "h1" );
const { chars , words , lines } = splitText ( element , {
type: "chars,words,lines"
});
You can split by any combination: "chars", "words", "lines", or "chars,words,lines"
Animate the elements
Use your preferred animation library to animate the split elements: animate (
chars ,
{ opacity: [ 0 , 1 ], y: [ 20 , 0 ] },
{ delay: stagger ( 0.02 ) }
);
Complete Example import { splitText } from "griffo" ;
import { animate , stagger } from "motion" ;
document . fonts . ready . then (() => {
const { chars } = splitText ( document . querySelector ( "h1" ), {
type: "chars"
});
animate (
chars ,
{ opacity: [ 0 , 1 ], y: [ 20 , 0 ] },
{ delay: stagger ( 0.02 ) }
);
});
Advanced: Masking for Reveal Animations Use the mask option to create reveal animations where characters slide in from behind a clip container: document . fonts . ready . then (() => {
const { words } = splitText ( element , {
type: "words" ,
mask: "words" // wraps each word in overflow: clip container
});
animate (
words ,
{ y: [ "100%" , "0%" ] },
{ delay: stagger ( 0.1 ) }
);
});
Cleanup: Reverting Restore the original HTML when animations complete: const { chars , revert } = splitText ( element , { type: "chars" });
const animation = animate ( chars , { opacity: [ 0 , 1 ] });
animation . finished . then (() => {
revert (); // restores original HTML
});
Or use the automatic revertOnComplete option: splitText ( element , {
type: "chars" ,
onSplit : ({ chars }) => animate ( chars , { opacity: [ 0 , 1 ] }),
revertOnComplete: true // automatically reverts when animation finishes
});
Import SplitText
Import the React component from griffo/react: import { SplitText } from "griffo/react" ;
import { animate , stagger } from "motion" ;
Wrap your text
Wrap any text element with <SplitText> and configure split options: < SplitText options = { { type: "words" } } >
< h1 > Hello World </ h1 >
</ SplitText >
Add animation callback
Use the onSplit callback to animate elements when they’re ready: < SplitText
options = { { type: "words" } }
onSplit = { ({ words }) => {
animate (
words ,
{ opacity: [ 0 , 1 ], y: [ 20 , 0 ] },
{ delay: stagger ( 0.05 ) }
);
} }
>
< h1 > Hello World </ h1 >
</ SplitText >
Complete Example import { SplitText } from "griffo/react" ;
import { animate , stagger } from "motion" ;
function Hero () {
return (
< SplitText
options = { { type: "words" } }
onSplit = { ({ words }) => {
animate (
words ,
{ opacity: [ 0 , 1 ], y: [ 20 , 0 ] },
{ delay: stagger ( 0.05 ) }
);
} }
>
< h1 > Hello World </ h1 >
</ SplitText >
);
}
Advanced: Viewport Animations Animate when text scrolls into view using the viewport and onViewportEnter props: < SplitText
options = { { type: "words" } }
initialStyles = { {
words: { opacity: 0 , transform: "translateY(20px)" }
} }
viewport = { { amount: 0.5 } }
onViewportEnter = { ({ words }) =>
animate ( words , { opacity: 1 , y: 0 }, { delay: stagger ( 0.05 ) })
}
resetOnViewportLeave
>
< p > Animates when scrolled into view </ p >
</ SplitText >
Advanced: Responsive Re-splitting Automatically re-split and re-animate when the container resizes: < SplitText
options = { { type: "words" , autoSplit: true } }
onSplit = { ({ words }) => {
animate ( words , { opacity: [ 0 , 1 ] }, { delay: stagger ( 0.05 ) });
} }
onResplit = { ({ words }) => {
// Re-animate after resize
animate ( words , { opacity: [ 0 , 1 ] }, { delay: stagger ( 0.05 ) });
} }
>
< h1 > Responsive Text </ h1 >
</ SplitText >
The waitForFonts prop is true by default, so React components automatically wait for fonts before splitting
Import SplitText from Motion
Import the Motion-enhanced component: import { SplitText } from "griffo/motion" ;
import { stagger } from "motion" ;
Use Motion animation props
Use familiar Motion props like initial, animate, and transition: < SplitText
initial = { { opacity: 0 , y: 20 } }
animate = { { opacity: 1 , y: 0 } }
transition = { { duration: 0.65 , delay: stagger ( 0.04 ) } }
options = { { type: "words" } }
>
< h1 > Hello World </ h1 >
</ SplitText >
Target specific split types
Apply different animations to chars, words, and lines: < SplitText
initial = { {
chars: { opacity: 0 , y: 10 },
words: { opacity: 0 }
} }
animate = { {
chars: { opacity: 1 , y: 0 },
words: { opacity: 1 }
} }
options = { { type: "chars,words" } }
>
< h1 > Multi-level Animation </ h1 >
</ SplitText >
Complete Example import { SplitText } from "griffo/motion" ;
import { stagger } from "motion" ;
function Hero () {
return (
< SplitText
initial = { { opacity: 0 , y: 20 } }
animate = { { opacity: 1 , y: 0 } }
transition = { { duration: 0.65 , delay: stagger ( 0.04 ) } }
options = { { type: "words" } }
>
< h1 > Hello World </ h1 >
</ SplitText >
);
}
Use whileInView for scroll-triggered animations: < SplitText
initial = { { opacity: 0 , y: 20 } }
whileInView = { { opacity: 1 , y: 0 } }
transition = { { delay: stagger ( 0.05 ) } }
viewport = { { once: true , amount: 0.5 } }
options = { { type: "words" } }
>
< p > Animates when scrolled into view </ p >
</ SplitText >
Advanced: Variants with Function Callbacks Use function variants to access element metadata like lineIndex: < SplitText
variants = { {
hidden: { chars: { opacity: 0 , y: 10 } },
visible: {
chars : ({ lineIndex }) => ({
opacity: 1 ,
y: 0 ,
transition: {
delay: stagger ( 0.02 , { startDelay: lineIndex * 0.15 }),
},
}),
},
} }
initial = "hidden"
animate = "visible"
options = { { type: "chars,lines" , mask: "lines" } }
>
< p > Per-line staggered reveal </ p >
</ SplitText >
Advanced: Masking for Reveal Animations Combine mask with Motion animations for smooth reveals: < SplitText
initial = { { y: "100%" } }
animate = { { y: "0%" } }
transition = { { delay: stagger ( 0.1 ) } }
options = { { type: "words" , mask: "words" } }
>
< h1 > Sliding Reveal </ h1 >
</ SplitText >
Common Patterns
Character-by-Character Fade In
const { chars } = splitText ( element , { type: "chars" });
animate ( chars , { opacity: [ 0 , 1 ] }, { delay: stagger ( 0.02 ) });
Word Slide Up
const { words } = splitText ( element , { type: "words" });
animate (
words ,
{ opacity: [ 0 , 1 ], y: [ 20 , 0 ] },
{ delay: stagger ( 0.05 ) }
);
Line-by-Line Reveal with Mask
const { lines } = splitText ( element , {
type: "lines" ,
mask: "lines"
});
animate (
lines ,
{ y: [ "100%" , "0%" ] },
{ delay: stagger ( 0.15 ) }
);
Important Notes
Ligatures are disabled: Griffo automatically sets font-variant-ligatures: none when splitting by characters, because ligatures can’t span multiple elements.
Accessibility is automatic: Headings get aria-label, generic elements get a screen-reader-only copy. Your animations remain accessible to assistive technology.
Use initialStyles for instant visibility: Set initial opacity or transforms via the initialStyles option to prevent flash of unstyled content:< SplitText
options = { { type: "words" } }
initialStyles = { {
words: { opacity: 0 , transform: "translateY(10px)" }
} }
onSplit = { ({ words }) => animate ( words , { opacity: 1 , y: 0 }) }
>
< h1 > No Flash </ h1 >
</ SplitText >
Next Steps
Core API Explore all options and return values for splitText()
React API Learn about lifecycle props, viewport detection, and more
Motion API Dive into variants, custom values, and animation props
Vanilla Guide Detailed guide for vanilla JavaScript usage