Docs
Go back
Component by
OV
OblivionTSX
typing-effect
"use client"
import { useEffect, useRef, useState } from "react"
import { cn } from "@/lib/utils"
export const componentMeta = {
name: "Typing Effect",
description: "A typewriter component that cycles through an array of words with a blinking cursor.",
props: {
words: { type: "string[]", default: '["React", "Next.js", "Tailwind"]', description: "Words to cycle through" },
speed: { type: "number", default: "80", description: "Typing speed in ms per character" },
deleteSpeed: { type: "number", default: "40", description: "Deletion speed in ms per character" },
pause: { type: "number", default: "1800", description: "Pause in ms after a word is fully typed" },
className: { type: "string", description: "Extra classes for the text" },
}
}
type Phase = "typing" | "pausing" | "deleting"
export default function TypingEffect({
words = ["React", "Next.js", "Tailwind CSS", "Framer Motion"],
speed = 80,
deleteSpeed = 40,
pause = 1800,
className,
}: {
words?: string[]
speed?: number
deleteSpeed?: number
pause?: number
className?: string
}) {
const [display, setDisplay] = useState("")
const [phase, setPhase] = useState<Phase>("typing")
const wordIdx = useRef(0)
const charIdx = useRef(0)
useEffect(() => {
const current = words[wordIdx.current % words.length]
if (phase === "typing") {
if (charIdx.current < current.length) {
const t = setTimeout(() => {
setDisplay(current.slice(0, charIdx.current + 1))
charIdx.current++
}, speed)
return () => clearTimeout(t)
} else {
const t = setTimeout(() => setPhase("pausing"), pause)
return () => clearTimeout(t)
}
}
if (phase === "pausing") {
const t = setTimeout(() => setPhase("deleting"), 100)
return () => clearTimeout(t)
}
if (phase === "deleting") {
if (charIdx.current > 0) {
const t = setTimeout(() => {
charIdx.current--
setDisplay(current.slice(0, charIdx.current))
}, deleteSpeed)
return () => clearTimeout(t)
} else {
wordIdx.current++
setPhase("typing")
}
}
}, [display, phase, words, speed, deleteSpeed, pause])
return (
<span className={cn("inline-flex items-center", className)}>
<span>{display}</span>
{/* Blinking cursor */}
<span
className="ml-[2px] inline-block w-[2px] h-[1.1em] rounded-sm bg-current animate-[ob-blink_1s_step-end_infinite]"
aria-hidden
/>
<style dangerouslySetInnerHTML={{ __html: `
@keyframes ob-blink { 0%,100%{opacity:1} 50%{opacity:0} }
`}} />
</span>
)
}
export function TypingEffectPreview() {
return (
<div className="flex w-full items-center justify-center bg-transparent px-8 py-14">
<div className="text-center space-y-4">
<p className="text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400">
Oblivion UI
</p>
<h1 className="text-4xl md:text-5xl font-bold tracking-tight text-zinc-900 dark:text-white">
Built for{" "}
<TypingEffect
words={["React", "Next.js", "Tailwind", "Framer Motion"]}
className="text-indigo-600 dark:text-indigo-400"
/>
</h1>
<p className="text-zinc-500 dark:text-zinc-400 max-w-sm mx-auto text-sm">
A typewriter that cycles smoothly through your key phrases.
</p>
</div>
</div>
)
}"use client"
import { useEffect, useRef, useState } from "react"
import { cn } from "@/lib/utils"
export const componentMeta = {
name: "Typing Effect",
description: "A typewriter component that cycles through an array of words with a blinking cursor.",
props: {
words: { type: "string[]", default: '["React", "Next.js", "Tailwind"]', description: "Words to cycle through" },
speed: { type: "number", default: "80", description: "Typing speed in ms per character" },
deleteSpeed: { type: "number", default: "40", description: "Deletion speed in ms per character" },
pause: { type: "number", default: "1800", description: "Pause in ms after a word is fully typed" },
className: { type: "string", description: "Extra classes for the text" },
}
}
type Phase = "typing" | "pausing" | "deleting"
export default function TypingEffect({
words = ["React", "Next.js", "Tailwind CSS", "Framer Motion"],
speed = 80,
deleteSpeed = 40,
pause = 1800,
className,
}: {
words?: string[]
speed?: number
deleteSpeed?: number
pause?: number
className?: string
}) {
const [display, setDisplay] = useState("")
const [phase, setPhase] = useState<Phase>("typing")
const wordIdx = useRef(0)
const charIdx = useRef(0)
useEffect(() => {
const current = words[wordIdx.current % words.length]
if (phase === "typing") {
if (charIdx.current < current.length) {
const t = setTimeout(() => {
setDisplay(current.slice(0, charIdx.current + 1))
charIdx.current++
}, speed)
return () => clearTimeout(t)
} else {
const t = setTimeout(() => setPhase("pausing"), pause)
return () => clearTimeout(t)
}
}
if (phase === "pausing") {
const t = setTimeout(() => setPhase("deleting"), 100)
return () => clearTimeout(t)
}
if (phase === "deleting") {
if (charIdx.current > 0) {
const t = setTimeout(() => {
charIdx.current--
setDisplay(current.slice(0, charIdx.current))
}, deleteSpeed)
return () => clearTimeout(t)
} else {
wordIdx.current++
setPhase("typing")
}
}
}, [display, phase, words, speed, deleteSpeed, pause])
return (
<span className={cn("inline-flex items-center", className)}>
<span>{display}</span>
{/* Blinking cursor */}
<span
className="ml-[2px] inline-block w-[2px] h-[1.1em] rounded-sm bg-current animate-[ob-blink_1s_step-end_infinite]"
aria-hidden
/>
<style dangerouslySetInnerHTML={{ __html: `
@keyframes ob-blink { 0%,100%{opacity:1} 50%{opacity:0} }
`}} />
</span>
)
}
export function TypingEffectPreview() {
return (
<div className="flex w-full items-center justify-center bg-transparent px-8 py-14">
<div className="text-center space-y-4">
<p className="text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400">
Oblivion UI
</p>
<h1 className="text-4xl md:text-5xl font-bold tracking-tight text-zinc-900 dark:text-white">
Built for{" "}
<TypingEffect
words={["React", "Next.js", "Tailwind", "Framer Motion"]}
className="text-indigo-600 dark:text-indigo-400"
/>
</h1>
<p className="text-zinc-500 dark:text-zinc-400 max-w-sm mx-auto text-sm">
A typewriter that cycles smoothly through your key phrases.
</p>
</div>
</div>
)
}Save to favoritesCopy to Figma
React
Typing Effect
A typewriter component that cycles through an array of words with a blinking cursor.
Installation
$ npx @cosmoo/oblivion add typing-effectDependencies
npm install Usage Example
tsx
import TypingEffect from "@/components/oblivion/typing-effect"
export default function Demo() {
return (
<div className="p-10 flex justify-center">
<TypingEffect />
</div>
)
}import TypingEffect from "@/components/oblivion/typing-effect"
export default function Demo() {
return (
<div className="p-10 flex justify-center">
<TypingEffect />
</div>
)
}