Logo

Voxlet UI

Animated Background

An animated background effect that highlights elements smoothly on hover or click, enhancing user interaction.

Bugatti Chiron

Quad-turbo W16, pure speed. A masterpiece of engineering designed for breathtaking acceleration and top speeds.

Lamborghini Revuelto

Hybrid V12 with insane power. Combines electrification with legendary Italian performance for unmatched thrill.

Ferrari SF90

Plug-in hybrid with 1000 HP. A blend of electrification and raw horsepower delivering unparalleled driving dynamics.

McLaren P1

F1 tech in a road car. A hybrid hypercar that merges Formula 1 innovations with everyday drivability and power.

Porsche 918 Spyder

Hybrid hypercar, ultimate grip. A fusion of cutting-edge technology and extreme handling precision on any road.

Koenigsegg Jesko

Revs up to 8500 RPM. Designed for extreme speed, aerodynamics, and record-breaking track performance.

Installation

  1. 1

    Install Dependencies

    npm i motion
  2. 2

    lib/utils.ts

    import { clsx, type ClassValue } from "clsx"
    import { twMerge } from "tailwind-merge"
        
    export function cn(...inputs: ClassValue[]) {
      return twMerge(clsx(inputs))
    }
  3. 3

    Copy the source code

    @/components/ui/animated-background.tsx
    'use client';
    import { cn } from '@/lib/utils';
    import { AnimatePresence, Transition, motion } from 'motion/react';
    import {
      Children,
      cloneElement,
      ReactElement,
      useEffect,
      useState,
      useId,
    } from 'react';
    
    export type AnimatedBackgroundProps = {
      children:
        | ReactElement<{ 'data-id': string }>[]
        | ReactElement<{ 'data-id': string }>;
      defaultValue?: string;
      onValueChange?: (newActiveId: string | null) => void;
      className?: string;
      transition?: Transition;
      enableHover?: boolean;
    };
    
    export default function AnimatedBackground({
      children,
      defaultValue,
      onValueChange,
      className,
      transition,
      enableHover = false,
    }: AnimatedBackgroundProps) {
      const [activeId, setActiveId] = useState<string | null>(null);
      const uniqueId = useId();
    
      const handleSetActiveId = (id: string | null) => {
        setActiveId(id);
    
        if (onValueChange) {
          onValueChange(id);
        }
      };
    
      useEffect(() => {
        if (defaultValue !== undefined) {
          setActiveId(defaultValue);
        }
      }, [defaultValue]);
    
      return Children.map(children, (child: any, index) => {
        const id = child.props['data-id'];
    
        const interactionProps = enableHover
          ? {
              onMouseEnter: () => handleSetActiveId(id),
              onMouseLeave: () => handleSetActiveId(null),
            }
          : {
              onClick: () => handleSetActiveId(id),
            };
    
        return cloneElement(
          child,
          {
            key: index,
            className: cn('relative inline-flex', child.props.className),
            'data-checked': activeId === id ? 'true' : 'false',
            ...interactionProps,
          },
          <>
            <AnimatePresence initial={false}>
              {activeId === id && (
                <motion.div
                  layoutId={`background-${uniqueId}`}
                  className={cn('absolute inset-0', className)}
                  transition={transition}
                  initial={{ opacity: defaultValue ? 1 : 0 }}
                  animate={{
                    opacity: 1,
                  }}
                  exit={{
                    opacity: 0,
                  }}
                />
              )}
            </AnimatePresence>
            <div className='z-10'>{child.props.children}</div>
          </>
        );
      });
    }
    

AnimatedBackgroundProps

PropTypeDefaultDescription
classNamestringundefinedAdditional custom class names for styling the animated background.
childrenReactElement<{ 'data-id': string }>[] | ReactElement<{ 'data-id': string }>-An array or a single React element with a `data-id` attribute used for tracking active elements.
defaultValuestringundefinedSpecifies which item should be highlighted by default.
onValueChange(newActiveId: string | null) => voidundefinedCallback function triggered when the active element changes.
transitionTransitionundefinedDefines the transition animation properties using the Motion library.
enableHoverbooleanfalseEnables hover-based activation instead of click-based interaction.
Brought to you by Voxlet