my/ui

Command Palette

Search for a command to run...

All components

Tracing Beam

effects

Aceternity UI component.

responsive · 680px

Install

Same command in any shadcn project — React (Vite/CRA), Next.js, Remix, Astro, and more:

$npx shadcn@latest add https://your-domain/r/tracing-beam.json

Usage

"use client";
import { TracingBeam } from "@/registry/aceternity-ui/tracing-beam";

const content = [
  {
    title: "Design Systems",
    body: "Build a consistent visual language across every product surface. Tokens, components, and documentation that scales with your team — from the first prototype to production.",
  },
  {
    title: "Motion & Interaction",
    body: "Smooth, purposeful animation turns static interfaces into living products. Spring physics, scroll-linked effects, and gesture-driven transitions that feel native on every device.",
  },
  {
    title: "Accessible by Default",
    body: "Keyboard navigation, screen-reader semantics, and colour contrast are not afterthoughts. Every component ships meeting WCAG 2.2 AA out of the box, no configuration needed.",
  },
  {
    title: "Open Source",
    body: "MIT-licensed and built in the open. Inspect the source, file an issue, or submit a pull request. The component library grows with its community.",
  },
];

export default function Demo() {
  return (
    <div className="w-full max-w-2xl mx-auto py-10 px-4">
      <TracingBeam>
        <div className="space-y-16">
          {content.map((item) => (
            <div key={item.title} className="space-y-3">
              <h2 className="text-xl font-semibold text-foreground">
                {item.title}
              </h2>
              <p className="text-muted-foreground leading-relaxed">{item.body}</p>
              <div className="h-40 rounded-lg bg-muted flex items-center justify-center">
                <span className="text-muted-foreground text-sm">{item.title}</span>
              </div>
            </div>
          ))}
        </div>
      </TracingBeam>
    </div>
  );
}

Component source

"use client";
import React, { useEffect, useRef, useState } from "react";
import {
  motion,
  useTransform,
  useScroll,
  useVelocity,
  useSpring,
} from "motion/react";
import { cn } from "@/lib/utils";

export const TracingBeam = ({
  children,
  className,
}: {
  children: React.ReactNode;
  className?: string;
}) => {
  const ref = useRef<HTMLDivElement>(null);
  const { scrollYProgress } = useScroll({
    target: ref,
    offset: ["start start", "end start"],
  });

  const contentRef = useRef<HTMLDivElement>(null);
  const [svgHeight, setSvgHeight] = useState(0);

  useEffect(() => {
    if (contentRef.current) {
      setSvgHeight(contentRef.current.offsetHeight);
    }
  }, []);

  const y1 = useSpring(
    useTransform(scrollYProgress, [0, 0.8], [50, svgHeight]),
    {
      stiffness: 500,
      damping: 90,
    },
  );
  const y2 = useSpring(
    useTransform(scrollYProgress, [0, 1], [50, svgHeight - 200]),
    {
      stiffness: 500,
      damping: 90,
    },
  );

  return (
    <motion.div
      ref={ref}
      className={cn("relative mx-auto h-full w-full max-w-4xl", className)}
    >
      <div className="absolute top-3 -left-4 md:-left-20">
        <motion.div
          transition={{
            duration: 0.2,
            delay: 0.5,
          }}
          animate={{
            boxShadow:
              scrollYProgress.get() > 0
                ? "none"
                : "rgba(0, 0, 0, 0.24) 0px 3px 8px",
          }}
          className="border-netural-200 ml-[27px] flex h-4 w-4 items-center justify-center rounded-full border shadow-sm"
        >
          <motion.div
            transition={{
              duration: 0.2,
              delay: 0.5,
            }}
            animate={{
              backgroundColor: scrollYProgress.get() > 0 ? "white" : "#10b981",
              borderColor: scrollYProgress.get() > 0 ? "white" : "#059669",
            }}
            className="h-2 w-2 rounded-full border border-neutral-300 bg-white"
          />
        </motion.div>
        <svg
          viewBox={`0 0 20 ${svgHeight}`}
          width="20"
          height={svgHeight} // Set the SVG height
          className="ml-4 block"
          aria-hidden="true"
        >
          <motion.path
            d={`M 1 0V -36 l 18 24 V ${svgHeight * 0.8} l -18 24V ${svgHeight}`}
            fill="none"
            stroke="#9091A0"
            strokeOpacity="0.16"
            transition={{
              duration: 10,
            }}
          ></motion.path>
          <motion.path
            d={`M 1 0V -36 l 18 24 V ${svgHeight * 0.8} l -18 24V ${svgHeight}`}
            fill="none"
            stroke="url(#gradient)"
            strokeWidth="1.25"
            className="motion-reduce:hidden"
            transition={{
              duration: 10,
            }}
          ></motion.path>
          <defs>
            <motion.linearGradient
              id="gradient"
              gradientUnits="userSpaceOnUse"
              x1="0"
              x2="0"
              y1={y1} // set y1 for gradient
              y2={y2} // set y2 for gradient
            >
              <stop stopColor="#18CCFC" stopOpacity="0"></stop>
              <stop stopColor="#18CCFC"></stop>
              <stop offset="0.325" stopColor="#6344F5"></stop>
              <stop offset="1" stopColor="#AE48FF" stopOpacity="0"></stop>
            </motion.linearGradient>
          </defs>
        </svg>
      </div>
      <div ref={contentRef}>{children}</div>
    </motion.div>
  );
};

Dependencies

motion

Source: Aceternity UI