← Back to Blog

Building a Scramble-on-Hover Text Effect

A tutorial on crafting the cyberpunk-style text scramble effect with pure React, timers, and proper cleanup — no external libs required.

Eduardo CalvoEduardo Calvo
··1 min read

In this tutorial, we'll build the Scramble Hover effect step by step. Hover the preview text after each step to see what you've unlocked.

Base Text

Start with a simple button rendering a string.

<button>{text}</button>

Scramble Function

Write a pure function that replaces every character (except spaces) with a random one.

const CHARACTERS = "ABC...xyz0123!@#".split("");function scrambleText(original: string) {  return original    .split("")    .map((c) =>      c === " " ? " " : CHARACTERS[Math.floor(Math.random() * CHARACTERS.length)]    )    .join("");}

Interval Loop

On hover, run setInterval to reshuffle the text every few milliseconds.

const [display, setDisplay] = useState(text);const handleMouseEnter = () => {  intervalRef.current = setInterval(() => {    setDisplay(scrambleText(text));  }, 30);};

Stop & Restore

Use setTimeout so the scramble lasts a fixed duration, then reset to the original text.

const handleMouseEnter = () => {  intervalRef.current = setInterval(() => {    setDisplay(scrambleText(text));  }, 30);  timeoutRef.current = setTimeout(() => {    clearInterval(intervalRef.current!);    setDisplay(text); // snap back to original  }, 600);};

Cleanup on Leave

Clear timers when the pointer leaves so animations don't leak or overlap.

const handleMouseLeave = () => {  clearInterval(intervalRef.current!);  clearTimeout(timeoutRef.current!);  setDisplay(text);};

Accessibility

Detect hover-capable devices and respect prefers-reduced-motion.

useEffect(() => {  const motion = matchMedia("(prefers-reduced-motion: reduce)");  const hover = matchMedia("(hover: hover) and (pointer: fine)");  setReduced(motion.matches);  setHasHover(hover.matches);}, []);const onEnter = reduced || !hasHover ? undefined : handleMouseEnter;
Live PreviewStep 1/6

Key Takeaways

After building this component, you've learned:

  1. Pure, stateless scramble functions are easy to test and reuse
  2. Preserve spaces so the silhouette of the text stays readable
  3. 30ms intervals feel glitchy without being unreadable; 600ms total is a natural duration
  4. Refs for timers — never put setInterval IDs in state
  5. Always clean up both timers on leave; otherwise hovers can stack
  6. Gate on hover:hover so touch devices don't fire the effect on tap

Install the Component

npx smoothui-cli add scramble-hover

Check out the full documentation for all props and variations.

Share:

More Posts