Random Access Components

Glow

Container wrapper with a hover glow effect, with composable grid glow effect for multiple cards

TypeScript iconTypeScript
React IconReact
Next.js IconNext.js

Demo

Grouped glow - cards have proximity effects

Self GlowLorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
CustomizableLorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.
AnimatedLorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
AccessibleLorem ipsum.

Standalone glow - cards don't interact with each other

Self Glow
Self Glow
Self Glow
"use client";
import { Glow } from "@/components/glow";
import { ArrowRight } from "lucide-react";
import { useRef } from "react";

function GlowDemo() {

    const gridRef = useRef<HTMLDivElement>(null);

    return (
        <div className="flex flex-col items-center justify-center gap-4 size-full">
            <h2 className="text-2xl font-serif text-foreground mb-0 mt-4">Glow Effect</h2>
            <p className="text-muted-foreground text-base">A card with a glow effect when you hover on it.</p>
            <div ref={gridRef} className="grid grid-cols-3 grid-rows-3 gap-2 size-full group/grid">
            <Glow parentRef={gridRef} glowColor="#087EA4" className="rounded-lg bg-muted-foreground/20 col-span-2 row-span-1">
                <div className="relative bg-muted size-full font-sans rounded-[7px] p-4 flex flex-col items-start text-left gap-2 overflow-hidden">
                    <span className="text-2xl text-foreground font-serif z-[2]">Efficient</span>
                    <span className="text-muted-foreground text-base z-[2]">Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.</span>
                    <ArrowRight className="size-4 mt-4 text-muted-foreground z-[2] group-hover/card:translate-x-2 group-hover/card:text-primary transition-transform duration-300" />
                </div>
            </Glow>
            <Glow parentRef={gridRef} glowColor="#9b59b6" className="rounded-lg bg-muted-foreground/20 col-span-1 row-span-2">
                <div className="relative bg-muted size-full font-sans rounded-[7px] p-4 flex flex-col items-start text-left gap-2 overflow-hidden">
                    <span className="text-2xl text-foreground font-serif z-[2]">Customizable</span>
                    <span className="text-muted-foreground text-base z-[2]">Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.</span>
                    <ArrowRight className="size-4 mt-4 text-muted-foreground z-[2] group-hover/card:translate-x-2 group-hover/card:text-primary transition-transform duration-300" />
                </div>
            </Glow>
            <Glow parentRef={gridRef} glowColor="#FFDD01" className="rounded-lg bg-muted-foreground/20 col-span-2 row-span-2">
                <div className="relative bg-muted size-full font-sans rounded-[7px] p-4 flex flex-col items-start text-left gap-2 overflow-hidden">
                    <span className="text-2xl text-foreground font-serif z-[2]">Animated</span>
                    <span className="text-muted-foreground text-base z-[2]">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.</span>
                    <ArrowRight className="size-4 mt-4 text-muted-foreground z-[2] group-hover/card:translate-x-2 group-hover/card:text-primary transition-transform duration-300" />
                </div>
            </Glow>
            <Glow parentRef={gridRef} glowColor="#38bdf8" className="rounded-lg bg-muted-foreground/20 col-span-1 row-span-1">
                <div className="relative bg-muted size-full font-sans rounded-[7px] p-4 flex flex-col items-start text-left gap-2 overflow-hidden">
                    <span className="text-2xl text-foreground font-serif z-[2]">Accessible</span>
                    <span className="text-muted-foreground text-base z-[2]">Lorem ipsum.</span>
                    <ArrowRight className="size-4 min-size- mt-4 text-muted-foreground z-[2] group-hover/card:translate-x-2 group-hover/card:text-primary transition-transform duration-300" />
                </div>
            </Glow>

            </div>
        </div>
    )
}

export { GlowDemo };

Installation

Copy and paste the following code into your project.

glow.tsx
"use client";
import { useEffect, useRef } from "react";
import { cn } from "@/lib/utils";

function Glow({
    children,
    glowColor = 'var(--primary)',
    parentRef = null,
    className,
    glowRadius = "400px",
    glowTransparency = "40%",
    ...props
}: {
    glowColor?: string;
    parentRef?: React.RefObject<HTMLDivElement | null> | null;
    glowRadius?: string;
    /**
     * The transparency of the glow. More transparency means more glow.
     * 
     * - With **100%**, Almost all the glow is visible through the border
     * - With **0%**, No glow is visible through the border
     * @default "40%"
     */
    glowTransparency?: string;
} & React.ComponentProps<'div'>) {

    const isStandalone = !parentRef;
    const cardRef = useRef<HTMLDivElement>(null);

    useEffect(() => {
        if (!cardRef.current) return;
        const refToUse = parentRef ? parentRef.current : cardRef.current;

        const handleMouseMove = (e: MouseEvent) => {
            const rect = cardRef.current?.getBoundingClientRect();
            if (!rect) return;
            cardRef.current?.style.setProperty('--glow-x', `${e.clientX - rect.left}px`);
            cardRef.current?.style.setProperty('--glow-y', `${e.clientY - rect.top}px`);
        };

        refToUse?.addEventListener('mousemove', handleMouseMove);
        return () => refToUse?.removeEventListener('mousemove', handleMouseMove);
    }, [cardRef, parentRef]);

    return (
        <div 
            ref={cardRef} 
            className={cn("relative p-px group/card", className)}
            {...props}
        >
            <div
                className={cn(
                    "absolute inset-0 rounded-lg opacity-0 transition-opacity duration-300 pointer-events-none",
                    isStandalone ? "group-hover/card:opacity-100" : "group-hover/grid:opacity-100"
                )}
                style={{
                    background: `radial-gradient(${glowRadius} circle at var(--glow-x, 50%) var(--glow-y, 50%), ${glowColor}, transparent ${glowTransparency})`,
                }}
            />
            {children}
        </div>
    );
}

export { Glow };

Usage

import { Glow } from "@/components/glow";

Standalone (single card)

When used without parentRef, each card tracks its own mouse position and the glow only appears on hover of that specific card.

<Glow glowColor="#087EA4" className="rounded-lg bg-muted-foreground/20">
    <div className="relative bg-muted rounded-[7px] p-4">
        <span className="text-foreground">Hover me</span>
    </div>
</Glow>

Grid (grouped cards)

Pass a shared parentRef to link multiple cards to the same mouse tracking area. The glow follows the cursor across all cards simultaneously. The parent container must have the group/grid Tailwind class.

const gridRef = useRef<HTMLDivElement>(null);

return (
    <div ref={gridRef} className="grid grid-cols-2 gap-4 group/grid">
        <Glow parentRef={gridRef} glowColor="#087EA4" className="rounded-lg bg-muted-foreground/20">
            <div className="relative bg-muted rounded-[7px] p-4">
                <span className="text-foreground">Card 1</span>
            </div>
        </Glow>
        <Glow parentRef={gridRef} glowColor="#9b59b6" className="rounded-lg bg-muted-foreground/20">
            <div className="relative bg-muted rounded-[7px] p-4">
                <span className="text-foreground">Card 2</span>
            </div>
        </Glow>
    </div>
);

Per-card hover interactions inside a grid

Each Glow always exposes a group/card Tailwind group. Use group-hover/card on children to create hover effects scoped to the individual card, even when the glow itself is linked to a grid.

<Glow parentRef={gridRef} glowColor="#087EA4" className="rounded-lg bg-muted-foreground/20">
    <div className="relative bg-muted rounded-[7px] p-4 flex items-center gap-2">
        <span className="text-foreground">Read more</span>
        <ArrowRight className="size-4 text-muted-foreground group-hover/card:translate-x-2 transition-transform duration-300" />
    </div>
</Glow>

API Reference

Glow

The card wrapper that renders the glow effect. Accepts all standard div props, additionally:

PropTypeDefaultDescription
glowColorstringvar(--primary)CSS color value for the glow gradient
parentRefRefObject<HTMLDivElement | null> | nullnullRef to a shared parent container for grouped glow. When null, the card tracks its own mouse position
glowRadiusstring"400px"Size of the radial gradient circle
glowTransparencystring"40%"Transparency stop of the gradient. Higher values show more glow through the border
classNamestring-Additional CSS classes
childrenReactNode-Glow container content

Tailwind groups

Glow relies on Tailwind's named group utilities for hover visibility:

Group nameApplied onPurpose
group/gridParent grid container (you add this)Required when using parentRef -- the glow overlay listens to this group's hover state
group/cardGlow root (always present)Lets children use group-hover/card:* for per-card hover effects

On this page