Flicker Grid
Grid squares that softly light up at random.
Always on
A living grid that hints at real-time activity.
components/ui/flicker-grid.tsx
"use client";
import { useEffect, useState, type ReactNode } from "react";
type FlickerGridProps = {
cols?: number;
rows?: number;
/** Squares lit at the same time */
active?: number;
children?: ReactNode;
className?: string;
};
export function FlickerGrid({ cols = 24, rows = 10, active = 14, children, className = "" }: FlickerGridProps) {
const [lit, setLit] = useState<Set<number>>(new Set());
useEffect(() => {
const total = cols * rows;
const tick = () => {
const next = new Set<number>();
while (next.size < active) next.add(Math.floor(Math.random() * total));
setLit(next);
};
tick();
const id = setInterval(tick, 900);
return () => clearInterval(id);
}, [cols, rows, active]);
return (
<div className={`relative isolate overflow-hidden ${className}`}>
<div
aria-hidden
className="absolute inset-0 -z-10 grid gap-px [mask-image:radial-gradient(ellipse_at_center,black_30%,transparent_75%)]"
style={{ gridTemplateColumns: `repeat(${cols}, 1fr)`, gridTemplateRows: `repeat(${rows}, 1fr)` }}
>
{Array.from({ length: cols * rows }, (_, i) => (
<span key={i} className={`rounded-[2px] transition-colors duration-700 ${lit.has(i) ? "bg-blue-500/40" : "bg-zinc-100"}`} />
))}
</div>
{children}
</div>
);
}
export default function Demo() {
return (
<FlickerGrid className="grid h-72 w-full max-w-3xl place-items-center rounded-3xl border border-zinc-200 bg-white">
<div className="rounded-2xl bg-white/80 px-8 py-5 text-center backdrop-blur-sm">
<h3 className="text-3xl font-semibold tracking-tight text-zinc-900">Always on</h3>
<p className="mt-1 text-zinc-500">A living grid that hints at real-time activity.</p>
</div>
</FlickerGrid>
);
}
Installation
Install the dependencies
$ npm i motion lucide-reactCopy the source into
components/ui/flicker-grid.tsxcomponents/ui/flicker-grid.tsx"use client"; import { useEffect, useState, type ReactNode } from "react"; type FlickerGridProps = { cols?: number; rows?: number; /** Squares lit at the same time */ active?: number; children?: ReactNode; className?: string; }; export function FlickerGrid({ cols = 24, rows = 10, active = 14, children, className = "" }: FlickerGridProps) { const [lit, setLit] = useState<Set<number>>(new Set()); useEffect(() => { const total = cols * rows; const tick = () => { const next = new Set<number>(); while (next.size < active) next.add(Math.floor(Math.random() * total)); setLit(next); }; tick(); const id = setInterval(tick, 900); return () => clearInterval(id); }, [cols, rows, active]); return ( <div className={`relative isolate overflow-hidden ${className}`}> <div aria-hidden className="absolute inset-0 -z-10 grid gap-px [mask-image:radial-gradient(ellipse_at_center,black_30%,transparent_75%)]" style={{ gridTemplateColumns: `repeat(${cols}, 1fr)`, gridTemplateRows: `repeat(${rows}, 1fr)` }} > {Array.from({ length: cols * rows }, (_, i) => ( <span key={i} className={`rounded-[2px] transition-colors duration-700 ${lit.has(i) ? "bg-blue-500/40" : "bg-zinc-100"}`} /> ))} </div> {children} </div> ); } export default function Demo() { return ( <FlickerGrid className="grid h-72 w-full max-w-3xl place-items-center rounded-3xl border border-zinc-200 bg-white"> <div className="rounded-2xl bg-white/80 px-8 py-5 text-center backdrop-blur-sm"> <h3 className="text-3xl font-semibold tracking-tight text-zinc-900">Always on</h3> <p className="mt-1 text-zinc-500">A living grid that hints at real-time activity.</p> </div> </FlickerGrid> ); }Use it
The file exports the component and a
Demoshowing typical usage. Import whichever you need.