Build
Create stunning user interfaces with modern components and beautiful animations.
Installation
Copy and paste the following code into your project.
components/ui/typewriter-hero.tsx
import * as React from "react";
import { cn } from "@/lib/utils";
export interface TypewriterHeroProps
extends React.HTMLAttributes<HTMLDivElement> {
title?: string;
description?: string;
words?: string[];
typingSpeed?: number;
deletingSpeed?: number;
pauseDuration?: number;
className?: string;
titleClassName?: string;
descriptionClassName?: string;
typingClassName?: string;
cursorClassName?: string;
align?: "left" | "center" | "right";
}
export function TypewriterHero({
title = "Welcome to",
description = "A modern and beautiful UI library for React",
words = ["Beautiful", "Modern", "Responsive", "Accessible"],
typingSpeed = 100,
deletingSpeed = 50,
pauseDuration = 2000,
className,
titleClassName,
descriptionClassName,
typingClassName,
cursorClassName,
align = "center",
...props
}: TypewriterHeroProps) {
const [currentText, setCurrentText] = React.useState("");
const [currentIndex, setCurrentIndex] = React.useState(0);
const [isDeleting, setIsDeleting] = React.useState(false);
const [isWaiting, setIsWaiting] = React.useState(false);
React.useEffect(() => {
const timeout = setTimeout(
() => {
if (isWaiting) {
setIsWaiting(false);
setIsDeleting(true);
return;
}
if (isDeleting) {
if (currentText === "") {
setIsDeleting(false);
setCurrentIndex((prev) => (prev + 1) % words.length);
} else {
setCurrentText((prev) => prev.slice(0, -1));
}
} else {
const targetWord = words[currentIndex];
if (currentText === targetWord) {
setIsWaiting(true);
} else {
setCurrentText((prev) => targetWord.slice(0, prev.length + 1));
}
}
},
isWaiting ? pauseDuration : isDeleting ? deletingSpeed : typingSpeed,
);
return () => clearTimeout(timeout);
}, [
currentText,
currentIndex,
isDeleting,
isWaiting,
words,
typingSpeed,
deletingSpeed,
pauseDuration,
]);
return (
<div
className={cn(
"relative w-full overflow-hidden py-24",
align === "center" && "text-center",
align === "right" && "text-right",
className,
)}
{...props}
>
<div className="relative mx-auto max-w-5xl px-4 sm:px-6 lg:px-8">
{title && (
<h1
className={cn(
"text-4xl font-extrabold tracking-tight sm:text-5xl lg:text-6xl",
titleClassName,
)}
>
{title}{" "}
<span
className={cn(
"bg-linear-to-r from-indigo-500 via-purple-500 to-pink-500 bg-clip-text text-transparent",
typingClassName,
)}
>
{currentText}
<span
className={cn(
"ml-1 inline-block h-[1em] w-[2px] animate-text-blink",
cursorClassName,
)}
aria-hidden="true"
/>
</span>
</h1>
)}
{description && (
<p
className={cn(
"mt-6 max-w-3xl text-xl text-gray-600 dark:text-gray-400",
align === "center" && "mx-auto",
align === "right" && "ml-auto",
descriptionClassName,
)}
>
{description}
</p>
)}
</div>
</div>
);
}
Update your globals.css
Add the following animations to your globals.css
:
@theme {
--animate-text-blink: text-blink 1.2s infinite ease-in-out;
@keyframes text-blink {
0%, 75%, 100%: { opacity: 1 },
75.1%, 95%: { opacity: 0 },
},
}
Update the import paths to match your project setup.
import TypewriterHero from "@/components/ui/typewriter-hero";
Usage
import TypewriterHero from "@/components/ui/typewriter-hero";
export default function Hero() {
return (
<TypewriterHero
title="Build"
description="Create stunning user interfaces with modern components."
words={["Faster", "Better", "Smarter", "Together"]}
typingSpeed={80}
deletingSpeed={40}
pauseDuration={2000}
/>
);
}
Examples
With Gradient Text
<TypewriterHero
title="Create"
description="Build beautiful interfaces with modern components."
words={["Faster", "Better", "Together"]}
typingClassName="bg-linear-to-r from-blue-600 via-indigo-600 to-purple-600 dark:from-blue-400 dark:via-indigo-400 dark:to-purple-400"
cursorClassName="bg-linear-to-r from-blue-600 via-indigo-600 to-purple-600 dark:from-blue-400 dark:via-indigo-400 dark:to-purple-400"
/>
Custom Alignment
<TypewriterHero
align="left"
title="Design"
description="Craft stunning user experiences that delight."
words={["Beautifully", "Efficiently", "Perfectly"]}
/>
Custom Animation Timing
<TypewriterHero
title="Develop"
description="Write clean, maintainable code with confidence."
words={["React", "Next.js", "TypeScript"]}
typingSpeed={50} // Faster typing
deletingSpeed={30} // Faster deleting
pauseDuration={3000} // Longer pause
/>
Props
Prop | Type | Default | Description |
---|---|---|---|
title | string | "Welcome to" | The main title text |
description | string | - | Secondary description text |
words | string[] | [] | Array of words to cycle through |
typingSpeed | number | 100 | Speed of typing animation in milliseconds |
deletingSpeed | number | 50 | Speed of deleting animation in milliseconds |
pauseDuration | number | 2000 | Duration to pause between words |
align | "left" | "center" | "right" | "center" | Text alignment |
className | string | - | Additional wrapper classes |
titleClassName | string | - | Additional title classes |
descriptionClassName | string | - | Additional description classes |
typingClassName | string | - | Additional typing text classes |
cursorClassName | string | - | Additional cursor classes |
Customization
Cursor Style
The cursor uses Tailwind's animation utilities for a realistic blinking effect. You can customize its appearance using the cursorClassName
prop:
<TypewriterHero
cursorClassName="w-[3px] bg-blue-500" // Thicker, blue cursor
/>
Text Gradients
Apply beautiful gradient effects to the typing text:
<TypewriterHero typingClassName="bg-linear-to-r from-pink-500 via-red-500 to-yellow-500 bg-clip-text" />
Dark Mode
The component automatically supports dark mode. You can customize dark mode styles using the appropriate Tailwind dark mode classes:
<TypewriterHero
titleClassName="text-gray-900 dark:text-white"
descriptionClassName="text-gray-600 dark:text-gray-300"
/>