Skip to main content

Text Scramble

The TextScramble component creates a captivating scramble animation effect where random characters rapidly cycle before resolving to the target text. Perfect for attention-grabbing headings, tech-themed interfaces, and dynamic content reveals.

Installation

npm install @craftdotui/components

Import

import TextScramble from "@craftdotui/components";

Usage

import TextScramble from "@craftdotui/components";

export default function Example() {
  return (
    <TextScramble>
      Hello, World!
    </TextScramble>
  );
}

Props

children
string
required
The final text to display after the scramble animation completes.
speed
number
default:"50"
Animation speed in milliseconds between character updates. Lower values = faster scrambling.
duration
number
default:"3"
Total duration of the scramble animation in seconds.
trigger
boolean
default:"true"
Whether to trigger the animation. Set to false to show final text without animation.
className
string
Additional CSS classes. Component uses font-mono by default.
characterSet
string
Custom set of characters to use during scrambling.
as
React.ElementType
default:"'span'"
HTML element type to render (span, div, h1, etc.).
onScrambleEnd
() => void
Callback function fired when scramble animation completes.

Animation Details

Algorithm

The component uses a progressive reveal algorithm:
  1. Starts with all characters scrambled
  2. Progressively reveals characters from left to right
  3. Characters to the left of currentIndex are finalized
  4. Characters to the right continue scrambling
  5. Spaces are preserved throughout
const scrambled = children
  .split("")
  .map((char, index) => {
    if (char === " ") return " ";
    return index < currentIndex
      ? char
      : characterSet[Math.floor(Math.random() * characterSet.length)];
  })
  .join("");

Timing

The animation interval runs at the specified speed, incrementing the reveal index:
setInterval(() => {
  // Update scrambled text
  currentIndex = Math.min(currentIndex + 1, children.length);
}, speed);
The animation automatically cleans up intervals on unmount to prevent memory leaks.

Examples

Hero Heading

import TextScramble from "@craftdotui/components";

export default function Hero() {
  return (
    <div className="min-h-screen flex items-center justify-center bg-black">
      <TextScramble 
        as="h1"
        className="text-6xl font-bold text-green-400"
        speed={40}
        duration={2.5}
      >
        SYSTEM ACCESS GRANTED
      </TextScramble>
    </div>
  );
}

Matrix-Style Effect

import TextScramble from "@craftdotui/components";

export default function MatrixText() {
  return (
    <TextScramble
      characterSet="アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン01234567890"
      className="text-green-400 text-2xl bg-black p-4"
      speed={25}
      duration={4}
    >
      Wake up, Neo
    </TextScramble>
  );
}

Sequential Reveals

import { useState, useEffect } from "react";
import TextScramble from "@craftdotui/components";

export default function SequentialReveal() {
  const [step, setStep] = useState(0);
  const messages = [
    "Initializing system...",
    "Loading modules...",
    "System ready.",
  ];

  return (
    <div className="space-y-4">
      {messages.map((msg, i) => (
        <TextScramble
          key={i}
          trigger={step >= i}
          speed={30}
          duration={1.5}
          onScrambleEnd={() => setStep(i + 1)}
          className="text-xl text-cyan-400"
        >
          {msg}
        </TextScramble>
      ))}
    </div>
  );
}

Interactive Button

import { useState } from "react";
import TextScramble from "@craftdotui/components";

export default function ScrambleButton() {
  const [isHovered, setIsHovered] = useState(false);
  
  return (
    <button
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
      className="px-8 py-4 bg-purple-600 text-white rounded-lg font-mono"
    >
      <TextScramble 
        trigger={isHovered}
        speed={20}
        duration={0.8}
      >
        LAUNCH MISSION
      </TextScramble>
    </button>
  );
}

Loading State

import TextScramble from "@craftdotui/components";

export default function LoadingState({ isLoading }) {
  return (
    <div className="flex items-center gap-3">
      <div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
      <TextScramble
        trigger={isLoading}
        speed={50}
        duration={2}
        className="text-white"
      >
        Processing data
      </TextScramble>
    </div>
  );
}
Combine with monospace fonts (font-mono) for the most authentic terminal/hacker aesthetic.

Character Set Examples

// Default (alphanumeric)
characterSet="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

// Binary only
characterSet="01"

// Hexadecimal
characterSet="0123456789ABCDEF"

// Symbols
characterSet="!@#$%^&*()_+-=[]{}|;:,.<>?"

// Unicode blocks
characterSet="█▓▒░"

// Katakana (Matrix style)
characterSet="アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン"

Performance Considerations

  • Uses setInterval for character updates
  • Automatically cleans up on unmount
  • Minimal re-renders (only when scrambled text changes)
  • Lightweight algorithm with O(n) complexity
Using very low speed values (< 10ms) may cause performance issues on slower devices.

Use Cases

  • Tech/cyberpunk themed websites
  • Loading states with personality
  • Hero section headings
  • Terminal/CLI interfaces
  • Gaming UIs
  • Data visualization reveals
  • Countdown timers
  • Easter eggs and hidden messages

Accessibility

  • Final text is always readable
  • Screen readers will announce the final text
  • Animation can be disabled with trigger={false}
  • Consider reduced motion preferences for sensitive users

Build docs developers (and LLMs) love