← Components/Animations & Cursors

TextReveal

textreveal-1779379671865.tsx
'use client'
import { useState, useEffect, useRef } from 'react'

const EFFECTS = ['stagger', 'typewriter', 'scramble', 'gradient'] as const
type Effect = typeof EFFECTS[number]

const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*'
const TEXT = 'Build faster with Empire UI'

function useTypewriter(text: string, speed = 60) {
  const [displayed, setDisplayed] = useState('')
  useEffect(() => {
    setDisplayed('')
    let i = 0
    const t = setInterval(() => {
      setDisplayed(text.slice(0, ++i))
      if (i >= text.length) clearInterval(t)
    }, speed)
    return () => clearInterval(t)
  }, [text, speed])
  return displayed
}

function ScrambleText({ text }: { text: string }) {
  const [displayed, setDisplayed] = useState(text)
  const frame = useRef(0)

  useEffect(() => {
    let iter = 0
    cancelAnimationFrame(frame.current)
    frame.current = requestAnimationFrame(function tick() {
      setDisplayed(text.split('').map((c, i) => {
        if (c === ' ') return ' '
        if (i < iter) return c
        return CHARS[Math.floor(Math.random() * CHARS.length)]
      }).join(''))
      if (iter < text.length) { iter += 0.3; frame.current = requestAnimationFrame(tick) }
    })
    return () => cancelAnimationFrame(frame.current)
  }, [text])

  return <span>{displayed}</span>
}

export default function TextReveal() {
  const [effect, setEffect] = useState<Effect>('stagger')
  const [key, setKey] = useState(0)
  const typewritten = useTypewriter(key > 0 ? TEXT : TEXT, 50)

  function replay() { setKey(k => k + 1) }

  const words = TEXT.split(' ')

  return (
    <div style={{ padding: 40, background: '#0A0A0A', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 32 }}>
      {/* Effect picker */}
      <div style={{ display: 'flex', gap: 6 }}>
        {EFFECTS.map(e => (
          <button key={e} onClick={() => { setEffect(e); replay() }} style={{ background: effect === e ? 'rgba(201,168,76,0.12)' : 'rgba(255,255,255,0.04)', border: '1px solid', borderColor: effect === e ? 'rgba(201,168,76,0.3)' : 'rgba(255,255,255,0.08)', color: effect === e ? '#C9A84C' : 'rgba(255,255,255,0.4)', borderRadius: 6, padding: '5px 12px', cursor: 'pointer', fontSize: 12 }}>{e}</button>
        ))}
      </div>

      {/* Animation */}
      <div style={{ fontSize: 36, fontWeight: 800, textAlign: 'center', minHeight: 48, lineHeight: 1.3 }}>
        {effect === 'stagger' && (
          <span key={key} style={{ display: 'inline' }}>
            {words.map((word, i) => (
              <span key={i} style={{ display: 'inline-block', marginRight: 10, animation: `slideUp 0.5s ${i * 0.1}s both ease`, color: '#F5F5F0' }}>{word}</span>
            ))}
          </span>
        )}
        {effect === 'typewriter' && (
          <span key={key} style={{ color: '#F5F5F0' }}>
            {typewritten}<span style={{ borderRight: '2px solid #C9A84C', marginLeft: 2, animation: 'blink 0.7s step-end infinite' }} />
          </span>
        )}
        {effect === 'scramble' && <span key={key} style={{ color: '#C9A84C', fontFamily: 'monospace' }}><ScrambleText text={TEXT} /></span>}
        {effect === 'gradient' && (
          <span key={key} style={{ background: 'linear-gradient(90deg, #C9A84C 0%, #6366f1 33%, #22c55e 66%, #C9A84C 100%)', backgroundSize: '300% auto', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', animation: 'gradMove 2s linear infinite' }}>{TEXT}</span>
        )}
      </div>

      <button onClick={replay} style={{ background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.1)', color: '#F5F5F0', borderRadius: 8, padding: '8px 20px', cursor: 'pointer', fontSize: 13 }}>Replay ↺</button>

      <style>{`
        @keyframes slideUp { from { opacity:0; transform:translateY(20px) } to { opacity:1; transform:translateY(0) } }
        @keyframes blink { 0%,100% { opacity:1 } 50% { opacity:0 } }
        @keyframes gradMove { to { background-position: 300% center } }
      `}</style>
    </div>
  )
}

Component info

CategoryAnimations & Cursors
Frameworkreact
TierFREE
Views0
Copies0

About

Animated text reveal with staggered word animation, typewriter, scramble, and gradient sweep effects

More from Animations & Cursors

'use client'
import { useState, useEffect, useRef } from 'react'

interface AnimatedNumberProps {
  target: number
  duration?: number
  prefix?: string
  suffix?: string
  decimals?: number
  onComplete?: () => void
}

function AnimatedNumber({ targ
CounterAnimation
Animations & Cursors
import React from 'react';
import { motion } from 'framer-motion';

interface MorphCursorProps {
  children: React.ReactNode;
}

const MorphCursor: React.FC<MorphCursorProps> = ({ children }) => {
  const [cursorType, setCursorType] = React.useState(
MorphCursor
Animations & Cursors
import { useEffect, useState } from 'react';

interface Dot {
  x: number;
  y: number;
  opacity: number;
}

const TrailCursor = () => {
  const [dots, setDots] = useState<Dot[]>([]);
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0
TrailCursor
Animations & Cursors
import { useState, useEffect } from 'react';
import { motion } from 'framer-motion';

interface PixelCursorProps {
  color: string;
}

const PixelCursor: React.FC<PixelCursorProps> = ({ color }) => {
  const [mousePosition, setMousePosition] = useSta
PixelCursor
Animations & Cursors