Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Text

Pro

Echo Text for React

Text with an echo, fainter copies trailing behind it that catch up one after another when it changes. For React with Base UI, when a word should reverberate.

Framework

Echo Text gives a line an echo: fainter copies of it trail behind, each a step further and fainter, like a word ringing out in a hall. When the text changes, the new words lead and the echoes catch up one after another, so for a moment the old word sounds behind the new one. With follow, the trail bends toward the pointer, each echo a little later than the one in front.

Use it for a hero word, a changing label, or an interactive headline that should feel like it has depth or sound. For a trail of colour, use Chromatic Text; for words that flip in place, Flip Words. The text in front is the real one; the echoes are copies that swap their words through a CSS keyframe animation the install adds to your stylesheet, and they follow the pointer with transitions, with no animation library. With reduced motion the echoes change at once and stay put.

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 { EchoText } from "@/components/ui/echo-text";
<h1>
  <EchoText text="Echo" />
</h1>

Demos

Change

Change text and the echoes catch up one after another: stagger is the time between one echo and the next, 80 ms by default. Here a timer walks through four greetings.

Preview width

Follow

follow bends the trail toward the pointer while it's over the text: each echo moves a little more slowly than the one in front, so moving the pointer draws them after it, and they settle back when it leaves. Touch doesn't move them.

Preview width

Offset

offsetX and offsetY are the step from one echo to the next, in em so it scales with the text: across, toward the end of the line, and down (a negative value goes up). echoes is how many there are, and decay how much of its strength each one keeps: 0.5 halves it every step.

Preview width

Accessibility

The text in front is real text, read once by screen readers and selectable; the echoes are copies hidden from them and from the pointer. The trail follows the text's own direction, so a right-to-left word echoes to the left. With reduced motion the echoes take a new word at once and don't follow the pointer; in forced-colors mode they're dropped.

FAQ

How do the echoes lag behind a change?

Each echo holds the word it had and the new one, one over the other, and a short CSS animation swaps them at its turn: the first echo after one stagger, the next after two, and so on. A new change restarts it, so a quick run of changes ripples back each time.

Can it make a trail that follows the cursor?

Yes, with follow. Each echo eases toward its new place more slowly than the one in front of it, so a moving pointer leaves a trail behind it, with no canvas and no frame loop. It answers the pointer only while it's over the text.

Why are the offsets in em?

So the echo scales with the text: the same offsetX gives a tight echo on a label and a wide one on a hero line. 0.12 is about a sixth of a letter's width.

Can the echoes be another colour?

They take the text's colour and fade by decay. To tint them, style their slot from outside, such as [&_[data-slot=echo-text-echo]]:text-primary on the text.

Do the echoes make the text harder to read?

The real text is always on top, at full strength, and each echo is fainter than the last. Keep the offset small and the echoes few on smaller text, or raise decay toward 0.3 for a quieter tail.

What happens with reduced motion?

The echoes stay where they are and take a new word at once, without the ripple; the pointer doesn't move them.

API Reference

EchoText

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

PropType
textstring

The text. Each change ripples back through the echoes.

decay?number

How much of each echo's strength the next one keeps, from 0 to 1. @default 0.5.

Default
0.5
echoes?number

How many echoes trail the text. @default 4.

Default
4
follow?boolean

Whether the echoes reach toward the pointer while it's over the text, each a little later than the one in front, like a trail.

Default
false
offsetX?number

How far each echo sits from the one in front of it, across, in em (toward the end of the line).

Default
0.12
offsetY?number

How far each echo sits from the one in front of it, down, in em. @default 0.

Default
0
stagger?number

The time between one echo catching up and the next, in milliseconds: when the text changes, and as they follow the pointer.

Default
80
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
--echo-text-offset-xstring

How far each echo sits across (0.12em).

--echo-text-offset-ystring

How far each echo sits down (0em).

--echo-text-staggerstring

The time between one echo catching up and the next (80ms).

All 97 text effects