Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Text

Free

Blur Text for React

Text that comes into focus a word at a time, each one fading in from a blur. For React with Base UI, when a headline should arrive as the page scrolls to it.

Framework

Blur Text brings a line in word by word: each word fades in from a soft blur and a small drop, one after the other, so the sentence reads itself onto the page. It plays once, when the text scrolls into view.

Use it for a hero headline or a section title that should land with a little ceremony. For words that appear without the blur, as if spoken, use Splitting Text; to type a line out, Typing Text. The motion is a CSS keyframe animation the install adds to your stylesheet, delayed for each word by its place in the line, with no animation library. With reduced motion the text is simply there.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Usage

import { BlurText } from "@/components/ui/blur-text";
<h1>
  <BlurText text="Components that make every launch feel effortless." />
</h1>

Demos

Characters

by="characters" animates each character instead of each word, with a shorter stagger by default (40 ms rather than 150 ms), for a short word or a title that should shimmer into place. Words still wrap whole: a line never breaks in the middle of one.

Preview width

Direction

direction is where each word comes in from: top, the default, drops it in from just above; bottom raises it from just below.

Preview width

Timing

duration is how long one word takes to come into focus, 700 ms by default, and stagger the time between one word and the next. A short duration and stagger suit a small label; a long one, a hero line read slowly.

Preview width

Accessibility

Screen readers read the whole line once, as plain text: the animated words are hidden from them, and a visually hidden copy carries the sentence. Words wrap whole, and the line keeps its direction on a right-to-left page. With reduced motion every word is in place from the start.

FAQ

When does the animation play?

Once, when at least a quarter of the text is in view: an IntersectionObserver on the text starts it, then stops watching. Pass trigger="mount" to play it as soon as the page shows it, for text that's always on screen.

Can it play again?

Give it a new key and React renders it fresh, so it plays again from the start, such as after the text changes.

How do I use it as a heading?

Put it inside your heading: <h1><BlurText text="…" /></h1>. Blur Text renders a span, so it takes the heading's size, weight and colour, and the heading stays the page's real heading.

What if JavaScript is off or slow to load?

With trigger="view", the server renders the words waiting, and a page without JavaScript shows the text at once. With trigger="mount" the animation runs from CSS alone, before the page's script loads.

How do I run something after it finishes?

Pass onComplete: it's called once, when the last word or character is in focus, so you can reveal the next line or a button after it.

What happens with reduced motion?

The words don't blur or move: the text is there from the start, fully readable.

API Reference

BlurText

<span> data-slot="blur-text"

PropType
textstring

The text.

by?"words" | "characters"

What it animates in: whole words, or each character. @default "words".

Type
"words" | "characters"
Default
"words"
direction?"top" | "bottom"

Where each word or character comes in from. @default "top".

Type
"top" | "bottom"
Default
"top"
duration?number

How long one word or character takes to come into focus, in milliseconds.

Default
700
onComplete?() → void

Called once the last word or character is in focus.

Returns
void
stagger?number

The time between one word or character and the next, in milliseconds.

Default
150 (words), 40 (characters)
trigger?"mount" | "view"

When it plays: as soon as it's on the page, or once it scrolls into view.

Type
"mount" | "view"
Default
"view"
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

AttributeType
data-last?string
data-view?string

"in" when visible is true, otherwise "out".

CSS variableType
--blur-text-durationstring

One word's fade into focus (700ms).

--blur-text-ystring

How far each word drops in from (-0.5em, from the top).

All 97 text effects