Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Text

Pro

Morph Text for React

Text that melts from one word into the next in place, letter by letter, through a soft blur. For React with Base UI, when a headline or a status should change without moving.

Framework

Morph Text changes one word into the next without going anywhere: the outgoing letters swell and dissolve while the next word's letters condense where they stood, one after the other, and both words blur through the middle of the change, so the text seems to melt into its replacement. It cycles through its texts on its own, holds the width of the widest, and pauses while the pointer is over it.

Use it for a hero word that changes in place, or a short status that moves through its stages. For words that roll through a window like a reel, use Rotating Text; to lift each word away and raise the next, Flip Words; to slide the same letters into new places, Anagram Text. A small timer counts the turns and CSS keyframe animations the install adds to your stylesheet morph the letters, with no animation library; the blur is on each whole word, not on every letter, so a long word costs no more than a short one. With reduced motion the words swap in place.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import { MorphText } from "@/components/ui/morph-text";
<h1>
  <MorphText texts={["Beautiful", "Composable", "Yours"]} />
</h1>

Demos

Label

At a label's size it reads as a status that changes. duration is how long each text stays, 2400 ms by default, and stagger the time between one letter morphing and the next, 25 ms; 0 morphs the whole word at once.

Preview width

Blur

--morph-text-blur is how soft the words get in the middle of the change, 8px by default. A small one keeps the letters legible as they cross; a large one, with a slower stagger, turns the change into a haze.

Preview width

Accessibility

Screen readers read every text once, as a list (Beautiful, Composable, Yours), from a visually hidden copy; the morphing letters are hidden from them, so they never hear a word change mid-sentence. The texts stop while the pointer is over them, for anyone reading slowly, and with reduced motion they swap without blurring or scaling.

FAQ

What happens when the texts have different lengths?

Each letter crosses over with the letter in the same place in the next text; where one text runs longer, its extra letters simply fade in or out. Nothing is padded, and every text is laid out in the same place, unseen, so the widest holds the space and the sentence around it never moves.

Is the blur expensive?

No more than two blurs at a time, whatever the length: the blur is on the outgoing and the incoming word as wholes, and only while they change. The letters move by opacity and scale alone.

How do I style the changing text?

Give it a className, such as font-bold text-primary: it applies to every text. The blur is a CSS variable, so [--morph-text-blur:4px] in the same className sets it.

Does it pause?

Yes, while the pointer is over it, and it waits a full duration again once the pointer leaves. With a single text it never morphs.

What do screen readers hear?

Every text once, separated by commas: "Beautiful, Composable, Yours".

What happens with reduced motion?

The texts still change, each replacing the last in place, without blurring, scaling or fading.

API Reference

MorphText

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

PropType
textsstring[]

The texts, shown in turn from the first; a text may be a few words.

duration?number

How long each text stays, in milliseconds. @default 2400.

Default
2400
stagger?number

The time between one letter morphing and the next, in milliseconds; 0 morphs the whole text at once.

Default
25
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-turn?string
CSS variableType
--morph-text-blurstring

How soft the words get mid-morph (8px).

--morph-text-staggerstring

The time between one letter morphing and the next (25ms).

All 97 text effects