Typewriter-style streaming text with a blinking caret.
import { useEffect, useState } from "react";
export function StreamingText({ text }: { text: string }) {
const [shown, setShown] = useState("");
useEffect(() => {
let i = 0;
const id = setInterval(() => {
i += 2;
setShown(text.slice(0, i));
if (i >= text.length) clearInterval(id);
}, 9);
return () => clearInterval(id);
}, [text]);
return <p className="prose">{shown}<span className="caret" /></p>;
}
/* StreamingText.css */
.prose { font-size: 14px; line-height: 19px; color: #1a1a1a; }
.caret { display: inline-block; width: 8px; height: 1.05em; margin-left: 2px; background: #0b0d12; vertical-align: text-bottom; animation: caret-blink 1s step-end infinite; }
@keyframes caret-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .caret { animation: none; } }
@media (prefers-color-scheme: dark) {
.prose { color: #f5f5f5; }
.caret { background: #f5f5f5; }
}