Typewriter Effect

A smooth, highly customizable typewriter effect for dynamic text storytelling.

Animation
Typewriter
UI
Writing
Draft

Write your dreams

Writing is the best way to express your thoughts


Installation

1. Install the following packages

npm
npm install @hugeicons/react @hugeicons/core-free-icons motion

2. Copy and paste the following code into your project

typewriter.tsx
1"use client"; 2 3import { Edit02Icon } from "@hugeicons/core-free-icons"; 4import { HugeiconsIcon } from "@hugeicons/react"; 5import { motion } from "motion/react"; 6import { useEffect, useState } from "react"; 7 8function TypewriterEffect() { 9 const textToType = "Building something beautiful together..."; 10 11 const [currentText, setCurrentText] = useState(""); 12 const [isDeleting, setIsDeleting] = useState(false); 13 14 useEffect(() => { 15 const timeout = setTimeout( 16 () => { 17 if (!isDeleting) { 18 if (currentText.length < textToType.length) { 19 setCurrentText(textToType.slice(0, currentText.length + 1)); 20 } else { 21 // Wait before starting to delete 22 setTimeout(() => setIsDeleting(true), 2000); 23 } 24 } else { 25 if (currentText.length > 0) { 26 setCurrentText(currentText.slice(0, -1)); 27 } else { 28 setIsDeleting(false); 29 } 30 } 31 }, 32 isDeleting ? 30 : 70 + Math.random() * 30, 33 ); 34 35 return () => clearTimeout(timeout); 36 }, [currentText, isDeleting, textToType]); 37 38 return ( 39 <div className="relative w-full max-w-[280px] bg-white/40 dark:bg-black/20 rounded-xl p-4 min-h-[140px] overflow-hidden"> 40 <div className="flex items-center gap-2 mb-3 border-b border-black/5 dark:border-white/5 pb-2"> 41 <HugeiconsIcon icon={Edit02Icon} size={12} className="text-muted-foreground" /> 42 <span className="text-[11px] font-medium text-muted-foreground uppercase tracking-wider">Draft</span> 43 </div> 44 45 <div className="relative text-sm leading-relaxed text-black/80 dark:text-white/80 wrap-break-word"> 46 <span className="inline-block"> 47 {currentText} 48 <motion.span 49 animate={{ opacity: [1, 0] }} 50 transition={{ 51 duration: 0.5, 52 repeat: Infinity, 53 repeatType: "reverse", 54 }} 55 className="ml-0.5 inline-block h-[1em] w-[2px] bg-violet-500 align-middle" 56 /> 57 </span> 58 </div> 59 </div> 60 ); 61} 62 63export function Typewriter() { 64 return ( 65 <motion.div 66 className="bg-white/50 dark:bg-black/40 rounded-md p-8 border border-gray-200/50 dark:border-white/10 min-h-[300px] flex flex-col shadow-[inset_-12px_-8px_40px_#46464620] max-w-[400px]" 67 initial={{ opacity: 0, y: 30 }} 68 whileInView={{ opacity: 1, y: 0 }} 69 viewport={{ once: true }} 70 transition={{ duration: 0.4, ease: "circOut" }} 71 data-clickable 72 > 73 <div className="flex-1 flex items-center justify-center"> 74 <TypewriterEffect /> 75 </div> 76 <div className="mt-6 flex flex-col"> 77 <div className="flex items-center gap-2 mb-1"> 78 <h3 className="font-semibold text-lg text-black dark:text-white">Write your dreams</h3> 79 </div> 80 <p className="text-muted-foreground text-sm leading-snug">Writing is the best way to express your thoughts</p> 81 </div> 82 </motion.div> 83 ); 84} 85 86export default Typewriter; 87