Skip to main content
This guide will help you create your first text animation with Griffo. Choose the approach that matches your project.

Choose Your Path

1

Import splitText

Import the core splitText function from Griffo:
import { splitText } from "griffo";
import { animate, stagger } from "motion";
2

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
3

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"
4

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
});

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

Build docs developers (and LLMs) love