Recipe

Text animations with Motion

Animate letter by letter, word by word, or line by line.

Go Pro

Playgrounds are part of PRO

Frontend.fyi Playgrounds let you take any code example and turn it into a live, editable sandbox โ€” so you can tweak the code, build on it, and learn by doing.

This feature is part of Frontend.fyi PRO, which gives you lifetime access to:

  • Interactive playgrounds (like this one)
  • Full courses (Framer Motion, CSS, and more coming)
  • Copy-paste UI recipes with video walkthroughs
  • Your own public profile to showcase projects and experiments (soon!)

If you're ready to go beyond just watching tutorials and actually build things yourself, PRO is for you.

Iโ€™ve covered Split Text Animations before, but those tutorials had a major downside: You had to manually split the text and handle accessibility yourself. Painful.

This hook handles all that for you โ€” and more. You can split text by characters, words, lines, or any combination you like.

Default usage

This hook builds on top of Motionโ€™s useAnimate() and returns:

  • scope: The ref to attach to the element you want to split
  • animate: The animate function, just like useAnimate() would return
  • isSplit: A boolean to check whether the text has been split (most of the times not needed)

By default, the hook splits by word, and each word gets the class .word. You can target these directly in your animations.

By default the hook will split on words.

Options

The hook accepts a configuration object with the following properties:

useSplitTextAnimation({
splitOn:
"word",
"word""line""char"["line", "char"]Any other combination as array

Defaults to "word". It is recommended to not split on more elements than needed, as this will unnecessarily increase the amount of DOM elements.

wordClassName:
"word",
string

The class name to use for the words, defaults to "word".

lineClassName:
"line",
string

The class name to use for the lines, defaults to "line".

charClassName:
"char",
string

The class name to use for the characters, defaults to "char".

});

Targeting split elements

Each split piece gets a class name. All of them can be targeted in your animations.

  • .word for words
  • .line for lines
  • .char for characters

Want custom class names? Pass them in the configuration object.

With Frontend.fyi PRO you will get lifetime access to all new courses and recipes released.

Fading in text โ€“ initial styles

The hook does not set initial styles for you. This means if you want to fade in the text, you are responsible for hiding the text initially.

The best way to do this is based on whether the aria-label attribute is present as you see in the example below. This makes sure the text is invisible until the split completes, then Motion takes over.

<div class="opacity-0 aria-[label]:opacity-100">
The text to be split
</div>
/** Assuming you want to split .content */
.content {
opacity: 0;
}
.content[aria-label] {
opacity: 1;
}

Notes

Accessibility

The hook automatically:

  • Sets aria-label to the original text content.
  • Ensures screen readers read the full text, not individual words/characters.

HTML inside text wonโ€™t work (yet)

This hook works with plain text only โ€” if your text includes HTML (like <strong> or <span> inside), it will not work.

More examples

Animate When In View

Combine with useInView() for view-based animations:

Optionally you can pass {once: true} to the useInView() hook to have the animation trigger only once.

I only animate the words when they are in view.

Words slide up

Sprinkling some animations on the web is a great way to make it more engaging.

Words slide in from right

Sprinkling some animations on the web is a great way to make it more engaging.

Masked letters slide down

By adding overflow: clip to every word, you can mask each letter. It will then appear as if the letters are sliding down out of nowhere.

Sprinkling some animations

Rotate and slide up lines

Sprinkling some animations on the web is a great way to make it more engaging.

Loop the animation

I mean, why not? If you want you can also loop the animation endlessly.

Oooink