VibeUI
ComponentsDocs
VibeUI

Components with taste, that you own. Copy the source, retint the tokens, keep every line.

Library

  • All components
  • Signature pieces
  • Text effects
  • Footers

Docs

  • Introduction
  • Installation
  • Theming
  • CLI

Project

  • GitHub
  • Agents and MCP
  • Issues
  • MIT license
© 2026 VibeUI · MIT licensed
  1. Components
  2. Text effects
  3. Scramble Text

Scramble Text

Text that scrambles through random glyphs and resolves, on hover or focus.

Text effectsClient component

Also fires on keyboard focus.

Install

bash
npx @agents-npm-packages/vibeui add scramble-text --dir ./src/components/ui

Import

tsx
import { ScrambleText } from "@/components/ui/scramble-text"

API

Generated from the component's TypeScript types, so it always matches the source. * marks a required prop.

<ScrambleText>

PropTypeDefaultDescription
text*string—The text to render.
classNamestring—Extra classes, merged with the component's own.
durationnumber700Animation length in milliseconds (or seconds where noted).

Source

src/components/ui/scramble-text.tsx
"use client";

import * as React from "react";
import { cn } from "@/lib/utils";

const CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#%&@";

/** Text that scrambles through random glyphs and resolves, on hover or focus. */
export function ScrambleText({
  text,
  className,
  duration = 700,
}: {
  text: string;
  className?: string;
  duration?: number;
}) {
  const [display, setDisplay] = React.useState(text);
  const frame = React.useRef(0);

  React.useEffect(() => () => cancelAnimationFrame(frame.current), []);

  const run = () => {
    const start = performance.now();
    cancelAnimationFrame(frame.current);
    const tick = (now: number) => {
      const t = Math.min(1, (now - start) / duration);
      const reveal = Math.floor(t * text.length);
      let out = "";
      for (let i = 0; i < text.length; i++) {
        out +=
          text[i] === " " || i < reveal
            ? text[i]
            : CHARS[Math.floor(Math.random() * CHARS.length)];
      }
      setDisplay(t < 1 ? out : text);
      if (t < 1) frame.current = requestAnimationFrame(tick);
    };
    frame.current = requestAnimationFrame(tick);
  };

  return (
    <button
      type="button"
      aria-label={text}
      onMouseEnter={run}
      onFocus={run}
      className={cn(
        "cursor-default whitespace-pre font-mono text-sm uppercase tracking-[0.08em] text-ink tabular-nums transition-colors hover:text-accent",
        className,
      )}
    >
      {display}
    </button>
  );
}

Exports ScrambleText. Peer dependencies: clsx, tailwind-merge.

PreviousRolling TextNextStagger Words