Something new is coming.Join the waitlist

Typewriter

PreviousNext

Accessible React typewriter text component. Cycles through a list of words, types/deletes each one, and exposes the live text to screen readers. No animation framework required.

Build

A small, accessible typewriter — switch the word list, the speed, or turn looping off for a one-shot intro.

Installation

npx shadcn@latest add https://tentui.com/r/typewriter.json

Usage

Drop Typewriter inline anywhere you'd put a <span>. Pass the words to cycle through and the component handles the rest.

import { Typewriter } from "@/components/typewriter";
 
export function Headline() {
  return (
    <h1 className="text-4xl font-semibold">
      Build{" "}
      <Typewriter
        words={["landing pages", "design systems", "MVPs"]}
        className="text-primary"
      />
    </h1>
  );
}

The animation is a plain setTimeout loop that swaps slices of the current word. No animation framework, no canvas, no measuring.

Patterns

One-shot intro

Set loop={false} to type out the last word and stop with the caret blinking. Good for hero subtitles where you only want the typing effect on load.

<Typewriter words={["welcome back, sourabh"]} loop={false} />

Custom speeds

Slow the deletion to match the typing, or speed both up for a snappier feel.

<Typewriter
  words={["fast", "faster"]}
  typingSpeed={40}
  deletingSpeed={40}
  holdDuration={800}
/>

No caret

Drop the caret entirely if it clashes with the surrounding type.

<Typewriter words={["minimal"]} caret={false} />

Props

PropTypeDefaultDescription
wordsstring[]Words to type out in sequence. At least one required.
typingSpeednumber60Milliseconds between keystrokes while typing.
deletingSpeednumber30Milliseconds between keystrokes while deleting.
holdDurationnumber1500Milliseconds to hold a fully-typed word before deleting.
loopbooleantrueCycle forever. Set false to stop at the last word.
caretbooleantrueShow a blinking caret after the text.
caretCharstring"|"Character used as the caret.
classNamestringForwarded to the outer <span>.

Accessibility

  • The typed text is wrapped in aria-live="polite" so screen readers announce the final word without spamming on every keystroke.
  • The caret is decorative — it carries aria-hidden="true" and uses Tailwind's animate-pulse for the blink.
  • Users who prefer reduced motion still see the text, just animated at the same speed; if you want to skip the typing entirely for them, render the final word directly behind a prefers-reduced-motion check.