shadcn.io is not affiliated with official shadcn/ui
Backspace Text types a line behind a caret, then corrects it: it deletes back only as far as the next line shares its start, and types the rest from there. "Build a website." loses "website." and becomes "Build an app.", which in turn becomes "Build a business.", so the line reads as one thought being revised rather than three lines in a row. A typo works the same way: type it, notice, backspace two letters, carry on.
Use it for a hero line that rewrites its last word, a message typed with a slip and fixed, or a command edited before it runs. To type and delete whole lines instead, use Typing Text; to swap a word without typing, Flip Words. A small timer moves it one keystroke at a time and a CSS keyframe animation blinks the caret, with no animation library; with reduced motion the lines show whole.
Installation
Usage
import { BackspaceText } from "@/components/ui/backspace-text";<BackspaceText text={["Build a website.", "Build an app."]} />Demos
Typo
loop={false} stops on the last line, so a typo is typed, fixed and left alone. A short pause is the moment the slip is noticed; delay is the wait before the first character.
Command
A line that shares all but its end with the next is only extended: here the closing quote goes and the message grows. cursor="block" draws a terminal's caret, deleteSpeed paces each backspace, and trigger="view" waits, caret blinking, until the text scrolls into view.
Accessibility
Screen readers hear the text, never the keystrokes: a visually hidden copy says the line it stops on, or every line once, in order, when it loops; the typed text and the caret are hidden from them. The caret blinks once a second, well under the three flashes a second that can trigger seizures, and stops with reduced motion, where each line shows whole, and a script that stops shows only its last line.
FAQ
How far back does it delete?
To the longest start the line shares with the next one. "I write code." and "I write apps." share "I write ", so only "code." goes. Lines with nothing in common are deleted whole, as Typing Text would.
Does it loop?
Yes, by default: after the last line it corrects back into the first, from what the two share, and goes round again. With loop={false} it stops on the last line, typed, and calls onComplete.
How do I tune the speed?
speed is the average time per typed character (60 ms), give or take jitter; deleteSpeed the time per backspace (40 ms); pause how long a whole line stays before it's corrected (1400 ms). A slower deleteSpeed makes the correction read as deliberate.
How is it different from Typing Text?
Typing Text types each line and deletes it whole before the next. Backspace Text keeps what the lines share and retypes only what changes, so it suits lines that differ by a word or a letter.
What do screen readers hear?
What the text settles on: with loop={false}, the last line only, so a deliberate typo is never read out. When it loops, every line once, one after the other.
API Reference
BackspaceText
<span> data-slot="backspace-text"
textstring[]
The lines in turn: each is typed, then deleted only as far back as it shares its start with the next, which is typed from there.
cursor?"bar" | "block" | "underscore" | "none"
The caret after the text. @default "bar".
- Type
"bar" | "block" | "underscore" | "none"- Default
"bar"
delay?number
The wait before the first character, in milliseconds. @default 0.
- Default
0
deleteSpeed?number
The time to delete one character, in milliseconds. @default 40.
- Default
40
jitter?number
How much each keystroke's wait varies, from 0 (even) to 1. @default 0.5.
- Default
0.5
loop?boolean
Whether the last line is corrected back into the first and it all starts over; otherwise it stops on the last line.
- Default
true
onComplete?() → void
Called once the last line is typed, when it doesn't loop.
- Returns
void
pause?number
How long a typed line stays before it's corrected, in milliseconds. @default 1400.
- Default
1400
speed?number
The time to type one character, on average, in milliseconds. @default 60.
- Default
60
trigger?"mount" | "view"
When the typing starts: as soon as the page shows it, or once it scrolls into view. @default "mount".
- Type
"mount" | "view"- Default
"mount"
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.
data-state?stringRelated
- Pro
Typing Text
EntranceType and delete whole lines
Flip Words
CyclingSwap a word without typing
- Pro
Terminal Text
EntranceCommands typed and run in a session
- Pro
Anagram Text
CyclingLetters that rearrange into a new word
- Pro
Morph Text
CyclingOne word melts into the next
Rotating Text
CyclingWords that roll through like a reel