NEW
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
Learn more
Skip to content
Docs

Animated Beam

Animated beam background section where the meteor beam of lights move through the line.

Loading...

Installation

CLI

pnpm dlx shadcn@latest add https://animata.design/r/background/animated-beam.json

Manual

Add to your CSS

@keyframes meteor {
  0% { transform: translateY(-20%) translateX(-50%); }
  100% { transform: translateY(300%) translateX(-50%); }
}
@theme {
  --animate-meteor: meteor var(--duration) var(--delay) ease-in-out infinite;
}

Run the following command

It will create a new file animated-beam.tsx inside the components/animata/background directory.

mkdir -p components/animata/background && touch components/animata/background/animated-beam.tsx

Paste the code

Open the newly created file and paste the following code:

"use client";
 
import { useEffect, useRef, useState } from "react";
 
import { cn } from "@/lib/utils";
 
function Beam({ index }: { index: number }) {
  const flag = index % 8 === 0;
  return (
    <div
      className={cn("h-full", {
        "[--duration:7s]": flag,
        "[--duration:11s]": !flag,
      })}
      style={{
        width: "6px",
        transform: "translateY(-20%)",
        "--delay": `${index * 0.5}s`,
        animation: "meteor var(--duration) var(--delay) ease-in-out infinite",
      }}
    >
      <div
        style={{
          clipPath: "polygon(54% 0, 54% 0, 60% 100%, 40% 100%)",
        }}
        className={cn("w-full", {
          "h-8": flag,
          "h-12": !flag,
        })}
      >
        <div className="h-full w-full bg-linear-to-b from-neutral-50/50 via-neutral-100 via-75% to-neutral-50" />
      </div>
    </div>
  );
}
 
function useGridCount() {
  const containerRef = useRef<HTMLDivElement>(null);
  const [count, setCount] = useState(0);
 
  useEffect(() => {
    const updateCount = () => {
      const rect = containerRef.current?.getBoundingClientRect();
      if (!rect) {
        return;
      }
      const width = rect.width;
      const cellSize = 40;
      setCount(Math.ceil(width / cellSize));
    };
 
    updateCount();
 
    // Can be debounced if needed
    window.addEventListener("resize", updateCount);
    return () => window.removeEventListener("resize", updateCount);
  }, []);
 
  return {
    count,
    containerRef,
  };
}
 
function Background() {
  const { count, containerRef } = useGridCount();
 
  return (
    <div
      ref={containerRef}
      className="z-0 absolute inset-0 flex h-full w-full flex-row justify-between bg-linear-to-t from-indigo-900 to-indigo-950"
    >
      <style>{`
        @keyframes meteor {
          0% { transform: translateY(-20%) translateX(-50%); }
          100% { transform: translateY(300%) translateX(-50%); }
        }
      `}</style>
      <div
        style={{
          background:
            "radial-gradient(50% 50% at 50% 50%,#072a39 0%,rgb(7,42,57) 50%,rgba(7,42,57,0) 100%)",
        }}
        className="absolute inset-0 top-1/2 h-full w-full rounded-full opacity-40"
      />
      {Array.from({ length: count }, (_, i) => (
        <div key={i} className="relative h-full w-px rotate-12 bg-gray-100/10">
          {(1 + i) % 4 === 0 && <Beam index={i + 1} />}
        </div>
      ))}
    </div>
  );
}
 
export default function AnimatedBeam({
  children,
  className,
}: {
  children: React.ReactNode;
  className?: string;
}) {
  return (
    <div className={cn("full-content relative w-full overflow-hidden", className)}>
      <Background />
      <div className="relative h-full w-full">{children}</div>
    </div>
  );
}

Credits

Built by hari