Skip to content
WANCRAFT

C.41Typography & Text

Typewriter

Types, holds and deletes a rotating list of phrases behind a blinking caret.

NewReactTailwindbasic

Overview

Typewriter cycles through a list of phrases: each one is typed, held with a blinking caret, deleted and replaced by the next. Keystrokes are written straight into the DOM from a timer chain, so React never re-renders per character, and an optional jitter varies the rhythm (with a short beat after spaces and punctuation) so it reads as typed rather than printed. Choose a bar, block or underscore caret, loop forever or stop on the last phrase, and reserve the width of the longest phrase so centred headlines never shift. The loop pauses offscreen and in background tabs.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/typewriter.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/typewriter.json

Method 02 · Copy and paste

  1. 1. No extra dependencies — React and Tailwind CSS only.
  2. 2. Copy the source from the Code tab into components/ui/typewriter.tsx.
  3. 3. Import it and adjust props.
Dependencies
None
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { Typewriter } from "@/components/ui/typewriter";

export function TypewriterExample() {
  const words = ["interfaces", "motion systems", "small details"];

  return (
    <Typewriter
      words={words}
      as="h2"
      className="text-4xl font-semibold tracking-tight"
    />
  );
}

03Props

PropTypeDefaultDescription
wordsstring[]—Phrases typed one after another (required).
prefixstring—Static text before the animated phrase.
as"span" | "p" | "h1" | "h2" | "h3" | "div""span"Element to render.
typeSpeednumber70Time per typed character, in ms.
deleteSpeednumber35Time per deleted character, in ms.
pausenumber1600Hold time once a phrase is complete, in ms.
loopbooleantrueCycle forever; when false, typing stops on the last phrase.
caret"bar" | "block" | "underscore""bar"Caret shape. It blinks while idle and stays solid while typing.
jitterbooleantrueVary keystroke timing (±40%) with a short beat after spaces and punctuation.
reserveWidthbooleanfalseReserve the width of the longest phrase so surrounding layout never shifts.
accentstring"#FF7448"Caret colour.
srTextstring—Text for assistive technology. Defaults to the prefix followed by every phrase.
...propsHTMLAttributes—Native attributes are forwarded to the root element.

04Features

  • 01Type → hold → delete cycle with independent speeds
  • 02Keystrokes written to the DOM from a timer chain — no React render per character
  • 03Optional human rhythm with a beat after spaces and punctuation
  • 04Bar, block and underscore carets; solid while typing, blinking while idle
  • 05Width-stable mode that reserves the longest phrase
  • 06Pauses offscreen and in background tabs
motion · continuousinput · viewport

05Accessibility

  • The full sentence (prefix and every phrase) is exposed once as visually hidden text; the animated text and caret are aria-hidden.
  • Screen readers never hear partial words or deletions.
  • With prefers-reduced-motion the first phrase is shown statically with a steady caret.

06Limitations

  • Phrases are split by code point, so complex emoji or combining sequences may type in more than one step.