Introducing Motion-Primitives Pro - Advanced components and templates to help you build a website that stands out.

Animated Background

The AnimatedBackground component visually highlights selected items by sliding a background into view when hovered over or clicked. This smooth transition helps users focus on the active item, making it ideal for interactive lists, menus, or navigations where clear selection feedback is important.

Examples

Animated Tabs

Animated Tabs Hover

Animated Card Background

Dialog

Enhances modal presentations.

Popover

For small interactive overlays.

Accordion

Collapsible sections for more content.

Collapsible

Collapsible sections for more content.

Drag to Reorder

Reorder items with drag and drop.

Swipe to Delete

Delete items with swipe gestures.

Segmented Control

Code

'use client';
import { cn } from '@/lib/utils';
import { AnimatePresence, Transition, motion } from 'framer-motion';
import {
  Children,
  cloneElement,
  ReactElement,
  useEffect,
  useState,
  useId,
} from 'react';

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),
        'aria-selected': activeId === id,
        '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>
        <span className='z-10'>{child.props.children}</span>
      </>
    );
  });
}

Please add:

Component API

PropTypeDefaultDescription
childrenReactElement with 'data-id' string attribute array or single elementThe content to be displayed within the animated background. Each child must have a unique data-id attribute to ensure correct functionality.
defaultValuestringThe default value to be used as the active item identifier.
onValueChange(newActiveId: string | null) => voidCallback function that is called when the active item changes.
classNamestringThe class name to apply to the animated background for custom styling.
transitionTransitionThe transition effect from framer-motion to apply when changing the active item.
enableHoverbooleanfalseEnables or disables the hover effect. When enableHover is true, spacing between children cannot be added.