Docs
Animated Grid

Animated Grid

An interactive grid pattern with animated hover effects

"The hardest choices require the strongest wills."

- Thanos, on choosing the right UI components

Installation

Copy and paste the following code into your project.

"use client"
 
import { useEffect, useId, useState } from "react"
import { AnimatePresence, motion } from "framer-motion"
 
import { cn } from "@/lib/utils"
 
interface GridPatternProps {
  width?: number
  height?: number
  x?: number
  y?: number
  strokeWidth?: number
  className?: string
  [key: string]: any
}
 
export function AnimatedGrid({
  width = 40,
  height = 40,
  x = 0,
  y = 0,
  strokeWidth = 1,
  className,
  ...props
}: GridPatternProps) {
  const id = useId()
  const [hoveredCell, setHoveredCell] = useState<{
    row: number
    col: number
  } | null>(null)
  const [mousePosition, setMousePosition] = useState<{
    x: number
    y: number
  } | null>(null)
 
  const handleMouseMove = (
    event: React.MouseEvent<SVGSVGElement, MouseEvent>
  ) => {
    const svg = event.currentTarget
    const rect = svg.getBoundingClientRect()
    const mouseX = event.clientX - rect.left
    const mouseY = event.clientY - rect.top
 
    const col = Math.floor(mouseX / width)
    const row = Math.floor(mouseY / height)
 
    setHoveredCell({ row, col })
    setMousePosition({ x: mouseX, y: mouseY })
  }
 
  const handleMouseLeave = () => {
    setHoveredCell(null)
    setMousePosition(null)
  }
 
  return (
    <svg
      aria-hidden="true"
      className={cn(
        "pointer-events-auto h-full w-full stroke-neutral-400/30 [--highlighted-grid-color:theme(colors.neutral.400/30)] dark:[--highlighted-grid-color:theme(colors.neutral.700/30)]",
        className
      )}
      {...props}
      onMouseMove={handleMouseMove}
      onMouseLeave={handleMouseLeave}
    >
      <defs>
        <pattern
          id={id}
          width={width}
          height={height}
          patternUnits="userSpaceOnUse"
          patternContentUnits="userSpaceOnUse"
          x={x}
          y={y}
        >
          <line x1="0" y1="0" x2={width} y2="0" strokeWidth={strokeWidth} />
          <line x1="0" y1="0" x2="0" y2={height} strokeWidth={strokeWidth} />
        </pattern>
      </defs>
      <rect width="100%" height="100%" strokeWidth={0} fill={`url(#${id})`} />
      <AnimatePresence>
        {hoveredCell && (
          <motion.rect
            key={`${hoveredCell.row}-${hoveredCell.col}`}
            x={hoveredCell.col * width}
            y={hoveredCell.row * height}
            width={width}
            height={height}
            initial={{ opacity: 0, scale: 0.8 }}
            animate={{ opacity: 1, scale: 1 }}
            exit={{ opacity: 0, scale: 0.8 }}
            transition={{ duration: 0.2 }}
            fill="var(--highlighted-grid-color)"
          />
        )}
      </AnimatePresence>
      {mousePosition && (
        <motion.circle
          cx={mousePosition.x}
          cy={mousePosition.y}
          r={width / 2}
          fill="none"
          stroke="var(--highlighted-grid-color)"
          strokeWidth={2}
          initial={{ scale: 0, opacity: 0 }}
          animate={{ scale: 1, opacity: 1 }}
          transition={{
            type: "spring",
            stiffness: 260,
            damping: 20,
          }}
        />
      )}
      {mousePosition && (
        <motion.circle
          cx={mousePosition.x}
          cy={mousePosition.y}
          r={width / 4}
          fill="var(--highlighted-grid-color)"
          initial={{ scale: 0, opacity: 0 }}
          animate={{ scale: 1, opacity: 1 }}
          transition={{
            type: "spring",
            stiffness: 260,
            damping: 20,
          }}
        />
      )}
    </svg>
  )
}
 
export AnimatedGrid

Update the import paths to match your project setup.

Usage

import AnimatedGrid from "@/components/ui/animatedgrid"
export default function AnimatedGridDemo() {
  return (
    <div className="relative h-screen w-full">
      <AnimatedGrid className="z-0" />
      <div className="absolute inset-0 flex items-center justify-center text-4xl font-bold text-black z-10 pointer-events-none dark:text-white">
         {/* Your content here */}
    </div>
  )
}

Props

The AnimatedGrid component accepts the following props:

  • width (number, optional): The width of each grid cell. Default is 40.
  • height (number, optional): The height of each grid cell. Default is 40.
  • x (number, optional): The x-offset of the grid pattern. Default is 0.
  • y (number, optional): The y-offset of the grid pattern. Default is 0.
  • strokeWidth (number, optional): The width of the grid lines. Default is 1.
  • className (string, optional): Additional CSS classes to apply to the component.

You can also pass any additional props that are valid for an SVG element.