Pixel Reveal.

A creative component that reveals content through a tiny pixelated transition.

Interactive Preview

MOTION DESIGN

Installation Guide

** 01. Dependencies **
pnpm add motion clsx tailwind-merge motion
** 02. Source Code **

Create components/pixel-reveal.tsx and paste the code below:

"use client";
import { cn } from "@/lib/utils";
import { motion } from "motion/react";
import React, { useEffect, useRef, useState, useMemo } from "react";
interface SquarePixelRevealProps {
  children: React.ReactNode;
  pixelSize?: number;
  className?: string;
  delay?: number;
  pixelColor?: string;
}
export const PixelReveal = ({
  children,
  pixelSize = 8,
  className,
  delay = 0,
  pixelColor = "bg-black dark:bg-white",
}: SquarePixelRevealProps) => {
  const containerRef = useRef<HTMLDivElement>(null);
  const [grid, setGrid] = useState({ cols: 0, rows: 0 });
  const [isInView, setIsInView] = useState(false);
  const isTailwindClass = pixelColor.startsWith("bg-");
  useEffect(() => {
    const updateGrid = () => {
      if (!containerRef.current) return;
      const { width, height } = containerRef.current.getBoundingClientRect();
      const cols = Math.ceil(width / pixelSize);
      const rows = Math.ceil(height / pixelSize);
      setGrid({ cols, rows });
    };
    updateGrid();
    window.addEventListener("resize", updateGrid);
    return () => window.removeEventListener("resize", updateGrid);
  }, [pixelSize]);
  const totalPixels = grid.cols * grid.rows;
  const pixelDelays = useMemo(() => {
    if (totalPixels === 0) return [];
    return Array.from({ length: totalPixels }, () => Math.random());
  }, [totalPixels]);
  return (
    <motion.div
      ref={containerRef}
      className={cn("relative inline-block", className)}
      onViewportEnter={() => setIsInView(true)}
      viewport={{ once: true, amount: 0.2 }}
    >
      <motion.div
        initial={{ opacity: 0 }}
        animate={isInView ? { opacity: 1 } : {}}
        transition={{ duration: 0.4, delay: delay + 0.2 }}
      >
        {children}
      </motion.div>
      {grid.cols > 0 && (
        <div
          className="pointer-events-none absolute inset-0 z-10"
          style={{
            display: "grid",
            gridTemplateColumns: `repeat(${grid.cols}, 1fr)`,
            gridTemplateRows: `repeat(${grid.rows}, 1fr)`,
          }}
        >
          {pixelDelays.map((randomFactor, i) => {
            const staggerDelay = delay + randomFactor * 0.8;
            return (
              <div
                key={i}
                className={cn(
                  "h-full w-full transition-opacity duration-0 will-change-opacity",
                  isTailwindClass ? pixelColor : "",
                  isInView ? "opacity-0" : "opacity-100"
                )}
                style={{
                  backgroundColor: !isTailwindClass ? pixelColor : undefined,
                  transitionDelay: `${staggerDelay}s`,
                }}
              />
            );
          })}
        </div>
      )}
    </motion.div>
  );
};
** 03. Usage Example **
import { PixelReveal } from "@/components/pixel-reveal";

export default function MyComponent() {
  return (
    <PixelReveal>
      <h1 className="text-4xl font-bold">Hello World</h1>
    </PixelReveal>
  );
}