Getting Started
Components
- All Components
- Animated Arrow
- Animated Counter
- Animated Save Button
- Animated Tabs
- Animated List/Grid View
- Bento Grid
- Border Beam
- Button
- Animated Copy Button
- Dock
- Animated 3D Folder
- Cursor-Tracking Glow Card
- Inline Edit Animation
- Animated OTP Input
- Infinite Marquee
- Animated Password Input
- Interactive Pie Chart
- Reveal on Scroll
- Tweet Card
- Typewriter
- Interactive World Map
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
| Prop | Type | Default | Description |
|---|---|---|---|
words | string[] | — | Words to type out in sequence. At least one required. |
typingSpeed | number | 60 | Milliseconds between keystrokes while typing. |
deletingSpeed | number | 30 | Milliseconds between keystrokes while deleting. |
holdDuration | number | 1500 | Milliseconds to hold a fully-typed word before deleting. |
loop | boolean | true | Cycle forever. Set false to stop at the last word. |
caret | boolean | true | Show a blinking caret after the text. |
caretChar | string | "|" | Character used as the caret. |
className | string | — | Forwarded 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'sanimate-pulsefor 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-motioncheck.