Types, pauses, deletes and cycles through phrases with a blinking caret.
Build
npx @agents-npm-packages/vibeui add typewriter --dir ./src/components/uiImport
import { Typewriter } from "@/components/ui/typewriter"Generated from the component's TypeScript types, so it always matches the source. * marks a required prop.
| Prop | Type | Default | Description |
|---|---|---|---|
| phrases* | string[] | — | Phrases to type in sequence. |
| className | string | — | Extra classes, merged with the component's own. |
| typingMs | number | 60 | Milliseconds per typed character. |
| pauseMs | number | 1600 | Milliseconds to hold a finished phrase. |
"use client";
import * as React from "react";
import { cn } from "@/lib/utils";
/** Types, pauses, deletes and cycles through phrases with a blinking caret. */
export function Typewriter({
phrases,
className,
typingMs = 60,
pauseMs = 1600,
}: {
phrases: string[];
className?: string;
typingMs?: number;
pauseMs?: number;
}) {
const [index, setIndex] = React.useState(0);
const [text, setText] = React.useState("");
const [deleting, setDeleting] = React.useState(false);
React.useEffect(() => {
if (!phrases.length) return;
const current = phrases[index % phrases.length] ?? "";
let delay: number;
let action: () => void;
if (!deleting && text === current) {
delay = pauseMs;
action = () => setDeleting(true);
} else if (deleting && text === "") {
delay = 280;
action = () => {
setDeleting(false);
setIndex((i) => (i + 1) % phrases.length);
};
} else {
delay = deleting ? typingMs / 2 : typingMs;
action = () =>
setText((prev) =>
deleting ? prev.slice(0, -1) : current.slice(0, prev.length + 1),
);
}
const t = window.setTimeout(action, delay);
return () => window.clearTimeout(t);
}, [text, deleting, index, phrases, typingMs, pauseMs]);
return (
<span className={cn("font-medium text-ink", className)}>
<span aria-live="off">{text}</span>
<span
aria-hidden
className="ml-0.5 inline-block h-[1em] w-[2px] translate-y-[0.14em] animate-blink bg-accent"
/>
</span>
);
}
Exports Typewriter. Peer dependencies: clsx, tailwind-merge.