Overview
The React <SplitText> component wraps the core splitText() function with React lifecycle management, automatic cleanup, and viewport-based animation triggers.
import { SplitText } from "griffo/react" ;
Bundle size: 8.23 kB (minified + brotli)
Basic Usage
import { SplitText } from "griffo/react" ;
import { animate , stagger } from "motion" ;
< SplitText
options = { { type: "words" } }
onSplit = { ({ words }) => {
animate ( words , { opacity: [ 0 , 1 ], y: [ 20 , 0 ] }, { delay: stagger ( 0.05 ) });
} }
>
< h1 > Hello World </ h1 >
</ SplitText > ;
<SplitText> requires exactly one child element. The child’s text content will be split.
Lifecycle Callbacks
onSplit
Called immediately after text is split:
< SplitText
options = { { type: "chars" } }
onSplit = { ({ chars , words , lines , revert }) => {
console . log ( `Split ${ chars . length } characters` );
// Return animation for revertOnComplete support
return animate ( chars , { opacity: [ 0 , 1 ] });
} }
>
< p > Animated text </ p >
</ SplitText >
onSplit
(result: SplitTextElements) => AnimationCallbackReturn
Receives { chars, words, lines, revert }. Return animation for auto-revert.
onResplit
Called when autoSplit triggers a re-split:
< SplitText
options = { { type: "words,lines" } }
autoSplit
onResplit = { ({ words , lines }) => {
console . log ( `Re-split into ${ lines . length } lines` );
animate ( words , { opacity: [ 0 , 1 ] });
} }
>
< p > Responsive text </ p >
</ SplitText >
onRevert
Called when text is reverted (manual or automatic):
< SplitText
onSplit = { ({ words }) => animate ( words , { opacity: [ 0 , 1 ] }) }
revertOnComplete
onRevert = { () => {
console . log ( "Text reverted to original HTML" );
} }
>
< h2 > Temporary split </ h2 >
</ SplitText >
Viewport Intersection
Trigger animations when elements enter/leave viewport:
onViewportEnter
< SplitText
options = { { type: "words" } }
viewport = { { amount: 0.5 } }
onViewportEnter = { ({ words }) => {
return animate ( words , { opacity: [ 0 , 1 ] }, { delay: stagger ( 0.05 ) });
} }
>
< p > Animates when scrolled into view </ p >
</ SplitText >
onViewportLeave
< SplitText
options = { { type: "chars" } }
viewport = { { amount: 0.3 } }
onViewportEnter = { ({ chars }) => animate ( chars , { opacity: 1 , scale: 1 }) }
onViewportLeave = { ({ chars }) => animate ( chars , { opacity: 0 , scale: 0.8 }) }
>
< h3 > Fades out when leaving viewport </ h3 >
</ SplitText >
Viewport Options
< SplitText
viewport = { {
once: true , // Only trigger once
amount: 0.5 , // 50% visibility required
margin: "0px 0px -100px 0px" , // IntersectionObserver rootMargin
} }
onViewportEnter = { ({ words }) => animate ( words , { opacity: [ 0 , 1 ] }) }
>
< p > Scroll-triggered animation </ p >
</ SplitText >
Only trigger onViewportEnter once
viewport.amount
number | 'some' | 'all'
default: 0
How much of the element must be visible
viewport.leave
number | 'some' | 'all'
default: 0
How much visibility is required to consider out of view
IntersectionObserver rootMargin
Initial Styles
Apply styles immediately after split:
< SplitText
options = { { type: "words" } }
initialStyles = { {
words: { opacity: 0 , transform: "translateY(20px)" },
} }
onViewportEnter = { ({ words }) => animate ( words , { opacity: 1 , y: 0 }) }
>
< h2 > Pre-styled words </ h2 >
</ SplitText >
Function-based Styles
< SplitText
options = { { type: "chars" } }
initialStyles = { {
chars : ( el , index ) => ({
opacity: 0 ,
transform: `translateY( ${ 20 + index * 2 } px)` ,
}),
} }
>
< p > Staggered initial positions </ p >
</ SplitText >
Reset on Viewport Leave
Re-apply initial styles when leaving viewport:
< 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 // Re-applies initialStyles
>
< p > Re-animates each time you scroll into view </ p >
</ SplitText >
resetOnViewportLeave is perfect for scroll-triggered animations that should reset when scrolling away.
Auto-revert on Complete
< SplitText
options = { { type: "words" } }
onSplit = { ({ words }) => animate ( words , { opacity: [ 0 , 1 ] }) }
revertOnComplete // Reverts when animation finishes
>
< h1 > Temporary split </ h1 >
</ SplitText >
revertOnComplete requires onSplit or onViewportEnter to return an animation (Motion, GSAP, or Promise).
Auto-split on Resize
< SplitText
options = { { type: "words,lines" } }
autoSplit
onResplit = { ({ lines }) => {
console . log ( `Re-split into ${ lines . length } lines` );
} }
>
< p > Responsive text that re-splits on resize </ p >
</ SplitText >
Font Loading
By default, <SplitText> waits for document.fonts.ready:
// Default behavior (recommended)
< SplitText waitForFonts = { true } >
< h1 > Waits for fonts </ h1 >
</ SplitText >
// Disable for immediate split
< SplitText waitForFonts = { false } >
< h1 > Splits immediately </ h1 >
</ SplitText >
The component is hidden with visibility: hidden while waiting for fonts.
Wrapper Element
Customize the wrapper element:
< SplitText
as = "section"
className = "my-wrapper"
style = { { padding: "2rem" } }
options = { { type: "words" } }
>
< h1 > Custom wrapper </ h1 >
</ SplitText >
Renders:
< section class = "my-wrapper" style = "padding: 2rem; visibility: visible;" >
< h1 > <!-- split text here --> </ h1 >
</ section >
Forwarding Refs
import { useRef } from "react" ;
const MyComponent = () => {
const wrapperRef = useRef < HTMLDivElement >( null );
return (
< SplitText ref = { wrapperRef } options = { { type: "words" } } >
< h1 > Ref forwarded to wrapper </ h1 >
</ SplitText >
);
};
Complete Example
import { SplitText } from "griffo/react" ;
import { animate , stagger } from "motion" ;
export default function AnimatedHeading () {
return (
< SplitText
options = { { type: "chars,words" , mask: "words" } }
initialStyles = { {
words: { overflow: "clip" },
chars: { transform: "translateY(100%)" },
} }
viewport = { { amount: 0.5 , once: true } }
onViewportEnter = { ({ chars }) =>
animate (
chars ,
{ y: 0 },
{
duration: 0.65 ,
delay: stagger ( 0.02 ),
}
)
}
revertOnComplete
>
< h1 > Scroll to reveal </ h1 >
</ SplitText >
);
}
Props Reference
Single React element containing text to split
onSplit
(result: SplitTextElements) => AnimationCallbackReturn
Called after split. Return animation for revertOnComplete.
onResplit
(result: SplitTextElements) => void
Called when autoSplit triggers
Called when text is reverted
onViewportEnter
(result: SplitTextElements) => AnimationCallbackReturn
Called when entering viewport
onViewportLeave
(result: SplitTextElements) => AnimationCallbackReturn
Called when leaving viewport
IntersectionObserver configuration
Re-split on container resize
Auto-revert when animation completes
Re-apply initialStyles when leaving viewport
Apply styles after split (static or function-based)
Wait for document.fonts.ready before splitting
as
keyof HTMLElementTagNameMap
default: "div"
Wrapper element type
Vanilla JS Framework-agnostic core API
Motion Declarative animations
Accessibility Screen reader support
Performance Optimization tips