Typewriter

Phrases typed out and deleted in a loop, with a caret and no layout shift.

Loading preview

Installation

npx shadcn@latest add @loomui/typewriter

Usage

import { Typewriter } from "@/components/ui/typewriter"
<p className="text-2xl">
  Built for <Typewriter words={["landing pages", "dashboards", "docs sites"]} />
</p>

The longest phrase is rendered invisibly underneath the visible one to reserve its width. Nothing around the typewriter reflows while it types, which is the usual reason this effect looks cheap.

Props

PropTypeDefaultDescription
wordsstring[]nonePhrases typed in order.
typingSpeednumber70Milliseconds per character while typing.
deletingSpeednumber40Milliseconds per character while deleting.
holdDelaynumber1600Milliseconds held on a finished phrase.
loopbooleantrueStart over after the last phrase.
caretbooleantrueShow the blinking caret.
disabledbooleanfalseRender the first phrase in full, no typing.
classNamestringnoneMerged onto the outer span.

Any other <span> prop is forwarded.

Accessibility

Every phrase is rendered once in a visually hidden span, and the typed copy is aria-hidden. A screen reader gets the full list as static text instead of a live region firing on every keystroke. Under prefers-reduced-motion the first phrase renders in full and nothing types.