Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Text

Pro

Inertia Text for React

Text you can drag that trails behind the pointer like a string of beads and springs home when let go. For React with Base UI, when a headline should invite a tug.

Framework

Inertia Text gives a line some weight. Grab it and drag: the letter you hold follows the pointer closely and each one further from it a little later, so the word stretches out behind a quick pull and bunches up again when you stop. Flick it and it carries on a little; let go and it springs home, past its place and back, the far letters last.

Use it for a playful hero word, a portfolio title or a 404 page that invites a tug. To pull letters towards the pointer without grabbing them, use Magnet Text; to stretch them like rubber, Elastic Text. Each move writes the pointer's offset to two CSS variables, and every letter follows by a CSS transition a little longer than its neighbour's, counted out from the one held: the lag is all in the transitions, with no physics library and no frame loop. With reduced motion the text moves as one with the pointer and snaps home.

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 { InertiaText } from "@/components/ui/inertia-text";
<h1>
  <InertiaText text="Drag me" />
</h1>

Demos

Stagger

stagger is how much later each letter catches up than the one before it, counted out from the letter you hold, 35 ms by default: a small value moves the word almost as one piece, a large one lets it trail like a rope. duration is how long the held letter takes to spring home, 700 ms; the others take a little longer.

Preview width

Words

by="words" moves whole words, for a sentence that follows the word you pull, or for scripts whose letters join, such as Arabic.

Preview width

Accessibility

Screen readers read the text once, from a visually hidden copy, never letter by letter. Dragging is decoration: it answers the pointer only, has no stop in the tab order and changes nothing, since the text always comes home, so keyboard and screen reader users miss nothing. With reduced motion the text follows the pointer as one piece and snaps home when let go, without the lag, the glide or the spring.

FAQ

How do the letters lag without a frame loop?

Each move writes the pointer's offset to two CSS variables that every letter reads, and each letter moves there by a CSS transition: the one you hold in 60 ms, and each one further out a stagger longer. A longer transition reaches the same place later, so the letters string out behind a fast pull and catch up when the pointer stops.

What does a flick do?

If the pointer is still moving when you let go, the text glides on in that direction, up to 120 pixels, slowing as it goes, then springs home. Let go of a still pointer and it springs home at once.

Does it work on touch screens?

Yes, sideways: a sideways drag moves the text, while an up or down swipe still scrolls the page, so a large headline never traps someone scrolling past it.

Can I drag it past the edge of the text?

Yes. The text keeps following until you let go, wherever the pointer goes, even outside the window.

Does dragging move anything around it?

No. The letters only move visually; the text keeps its place in the layout the whole time.

What happens with reduced motion?

The text still follows a drag, as one piece, and snaps home when you let go: no trail, no glide and no spring.

API Reference

InertiaText

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

PropType
textstring

The text.

by?"characters" | "words"

What trails on its own: each character, or each word (for scripts whose letters join, such as Arabic).

Type
"characters" | "words"
Default
"characters"
duration?number

How long the letter you held takes to spring home once you let go, in milliseconds; the others take a little longer.

Default
700
stagger?number

How much later each letter catches up than the one before it, counting out from the letter you hold, in milliseconds.

Default
35
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

Forwarded to the part.

AttributeType
data-index?string

Mirrors index.

CSS variableType
--inertia-text-durationstring

The held letter's spring home (700ms).

--inertia-text-staggerstring

How much later each letter out catches up (35ms).

All 97 text effects