Introducing Aceternity UI Pro - Premium component packs and templates to build awesome websites.
Logo

Glowing Effect

A border glowing effect that adapts to any container or card, as seen on Cursor's website.

Open in
  • Do things the right way

    Running out of copy so I'll write anything.

  • The best AI code editor ever.

    Yes, it's true. I'm not even kidding. Ask my mom if you don't believe me.

  • You should buy Aceternity UI Pro

    It's the best money you'll ever spend

  • This card is also built by Cursor

    I'm not even kidding. Ask my mom if you don't believe me.

  • Coming soon on Aceternity UI

    I'm writing the code as I record this, no shit.

Installation

Install dependencies

npm i motion clsx tailwind-merge

Add util file

lib/utils.ts
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
 
export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

Copy the source code

components/ui/glowing-effect.tsx

"use client";
 
import { memo, useCallback, useEffect, useRef } from "react";
import { cn } from "@/lib/utils";
import { animate } from "motion/react";
 
interface GlowingEffectProps {
  blur?: number;
  inactiveZone?: number;
  proximity?: number;
  spread?: number;
  variant?: "default" | "white";
  glow?: boolean;
  className?: string;
  disabled?: boolean;
  movementDuration?: number;
  borderWidth?: number;
}
const GlowingEffect = memo(
  ({
    blur = 0,
    inactiveZone = 0.7,
    proximity = 0,
    spread = 20,
    variant = "default",
    glow = false,
    className,
    movementDuration = 2,
    borderWidth = 1,
    disabled = true,
  }: GlowingEffectProps) => {
    const containerRef = useRef<HTMLDivElement>(null);
    const lastPosition = useRef({ x: 0, y: 0 });
    const animationFrameRef = useRef<number>(0);
 
    const handleMove = useCallback(
      (e?: MouseEvent | { x: number; y: number }) => {
        if (!containerRef.current) return;
 
        if (animationFrameRef.current) {
          cancelAnimationFrame(animationFrameRef.current);
        }
 
        animationFrameRef.current = requestAnimationFrame(() => {
          const element = containerRef.current;
          if (!element) return;
 
          const { left, top, width, height } = element.getBoundingClientRect();
          const mouseX = e?.x ?? lastPosition.current.x;
          const mouseY = e?.y ?? lastPosition.current.y;
 
          if (e) {
            lastPosition.current = { x: mouseX, y: mouseY };
          }
 
          const center = [left + width * 0.5, top + height * 0.5];
          const distanceFromCenter = Math.hypot(
            mouseX - center[0],
            mouseY - center[1]
          );
          const inactiveRadius = 0.5 * Math.min(width, height) * inactiveZone;
 
          if (distanceFromCenter < inactiveRadius) {
            element.style.setProperty("--active", "0");
            return;
          }
 
          const isActive =
            mouseX > left - proximity &&
            mouseX < left + width + proximity &&
            mouseY > top - proximity &&
            mouseY < top + height + proximity;
 
          element.style.setProperty("--active", isActive ? "1" : "0");
 
          if (!isActive) return;
 
          const currentAngle =
            parseFloat(element.style.getPropertyValue("--start")) || 0;
          let targetAngle =
            (180 * Math.atan2(mouseY - center[1], mouseX - center[0])) /
              Math.PI +
            90;
 
          const angleDiff = ((targetAngle - currentAngle + 180) % 360) - 180;
          const newAngle = currentAngle + angleDiff;
 
          animate(currentAngle, newAngle, {
            duration: movementDuration,
            ease: [0.16, 1, 0.3, 1],
            onUpdate: (value) => {
              element.style.setProperty("--start", String(value));
            },
          });
        });
      },
      [inactiveZone, proximity, movementDuration]
    );
 
    useEffect(() => {
      if (disabled) return;
 
      const handleScroll = () => handleMove();
      const handlePointerMove = (e: PointerEvent) => handleMove(e);
 
      window.addEventListener("scroll", handleScroll, { passive: true });
      document.body.addEventListener("pointermove", handlePointerMove, {
        passive: true,
      });
 
      return () => {
        if (animationFrameRef.current) {
          cancelAnimationFrame(animationFrameRef.current);
        }
        window.removeEventListener("scroll", handleScroll);
        document.body.removeEventListener("pointermove", handlePointerMove);
      };
    }, [handleMove, disabled]);
 
    return (
      <>
        <div
          className={cn(
            "pointer-events-none absolute -inset-px hidden rounded-[inherit] border opacity-0 transition-opacity",
            glow && "opacity-100",
            variant === "white" && "border-white",
            disabled && "!block"
          )}
        />
        <div
          ref={containerRef}
          style={
            {
              "--blur": `${blur}px`,
              "--spread": spread,
              "--start": "0",
              "--active": "0",
              "--glowingeffect-border-width": `${borderWidth}px`,
              "--repeating-conic-gradient-times": "5",
              "--gradient":
                variant === "white"
                  ? `repeating-conic-gradient(
                  from 236.84deg at 50% 50%,
                  var(--black),
                  var(--black) calc(25% / var(--repeating-conic-gradient-times))
                )`
                  : `radial-gradient(circle, #dd7bbb 10%, #dd7bbb00 20%),
                radial-gradient(circle at 40% 40%, #d79f1e 5%, #d79f1e00 15%),
                radial-gradient(circle at 60% 60%, #5a922c 10%, #5a922c00 20%), 
                radial-gradient(circle at 40% 60%, #4c7894 10%, #4c789400 20%),
                repeating-conic-gradient(
                  from 236.84deg at 50% 50%,
                  #dd7bbb 0%,
                  #d79f1e calc(25% / var(--repeating-conic-gradient-times)),
                  #5a922c calc(50% / var(--repeating-conic-gradient-times)), 
                  #4c7894 calc(75% / var(--repeating-conic-gradient-times)),
                  #dd7bbb calc(100% / var(--repeating-conic-gradient-times))
                )`,
            } as React.CSSProperties
          }
          className={cn(
            "pointer-events-none absolute inset-0 rounded-[inherit] opacity-100 transition-opacity",
            glow && "opacity-100",
            blur > 0 && "blur-[var(--blur)] ",
            className,
            disabled && "!hidden"
          )}
        >
          <div
            className={cn(
              "glow",
              "rounded-[inherit]",
              'after:content-[""] after:rounded-[inherit] after:absolute after:inset-[calc(-1*var(--glowingeffect-border-width))]',
              "after:[border:var(--glowingeffect-border-width)_solid_transparent]",
              "after:[background:var(--gradient)] after:[background-attachment:fixed]",
              "after:opacity-[var(--active)] after:transition-opacity after:duration-300",
              "after:[mask-clip:padding-box,border-box]",
              "after:[mask-composite:intersect]",
              "after:[mask-image:linear-gradient(#0000,#0000),conic-gradient(from_calc((var(--start)-var(--spread))*1deg),#00000000_0deg,#fff,#00000000_calc(var(--spread)*2deg))]"
            )}
          />
        </div>
      </>
    );
  }
);
 
GlowingEffect.displayName = "GlowingEffect";
 
export { GlowingEffect };

Example

Here is another example usage of the same component with different props.

Open in
  • Do things the right way

    Running out of copy so I'll write anything.

  • The best AI code editor ever.

    Yes, it's true. I'm not even kidding. Ask my mom if you don't believe me.

  • You should buy Aceternity UI Pro

    It's the best money you'll ever spend

  • This card is also built by Cursor

    I'm not even kidding. Ask my mom if you don't believe me.

  • Coming soon on Aceternity UI

    I'm writing the code as I record this, no shit.

Props

PropTypeDefaultDescription
blurnumber0The amount of blur applied to the glowing effect in pixels
inactiveZonenumber0.7The radius multiplier for the center zone where the effect is disabled. Value between 0 and 1
proximitynumber0The distance in pixels beyond the element's bounds where the effect remains active
spreadnumber20The angular spread of the glowing effect in degrees
variant"default" | "white""default"The color variant of the effect. "default" uses a multi-color gradient, while "white" uses a black and white scheme
glowbooleanfalseWhen true, forces the effect to be visible regardless of hover state
classNamestringundefinedAdditional CSS classes to apply to the effect container
disabledbooleantrueWhen true, disables the interactive glowing effect
movementDurationnumber2The duration of the glow movement animation in seconds
borderWidthnumber1The width of the glowing border in pixels

This component was inspired by Cursor's Enterprise page.

Get beautiful, hand-crafted templates and components with Aceternity UI Pro

Professional, beautiful and elegant templates for your business. Get the best component packs and templates with Aceternity UI Pro.

Go Pro

The work that Manu did laid the foundation of online education that we provide today. The website he built for us is used by thousands of students every day. He took the requirements and built the ...

Jagvinder Kour

Chairperson at Golden Bells Academy

A product by Aceternity
Building in public at @mannupaaji