Text morph

A label that changes without a cut. Letters the old and new text share slide to their new places on a spring, the ones that leave blur away where they stood, and the new ones bloom in after a beat. The box eases to its new width, so the button around it resizes with the words. Change it again mid-flight and it carries on from where everything is.

Installation

Terminalbash
npx @neelshha/ui@latest add text-morph

Usage

page.tsxtsx
import { TextMorph } from "@/components/ui/text-morph";

<Button onClick={deploy}>
  <TextMorph>{deploying ? "Deploying" : "Deploy"}</TextMorph>
</Button>

It's for one line: button labels, counts, statuses. Letters are matched by character, so the second "o" in the old text is the second "o" in the new one. Screen readers get the text itself, not the letters.

Counts

Only the number changes, so the words beside it hold still. Going from one to two, the plural s blooms in at the end.

3 new messages

page.tsxtsx
<TextMorph>{`${count} new ${count === 1 ? "message" : "messages"}`}</TextMorph>

Next to an icon

The icon swaps at once and the label morphs, and the button eases to its new width.

page.tsxtsx
<Button onClick={copy}>
  {copied ? <Check /> : <Copy />}
  <TextMorph>{copied ? "Copied" : "Copy command"}</TextMorph>
</Button>

API

Other props go to the outer span. It measures itself again whenever its size changes, so a web font that loads late is picked up before the next change. With reduced motion, the new text appears at once.

PropTypeDescription
childrenstringThe text. A new value morphs from the old one: letters the two share slide to their new places, and the rest blur out and in.