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
Prop | Type | Default | Description |
---|---|---|---|
children | ReactElement with 'data-id' string attribute array or single element | The content to be displayed within the animated background. Each child must have a unique data-id attribute to ensure correct functionality. | |
defaultValue | string | The default value to be used as the active item identifier. | |
onValueChange | (newActiveId: string | null) => void | Callback function that is called when the active item changes. | |
className | string | The class name to apply to the animated background for custom styling. | |
transition | Transition | The transition effect from framer-motion to apply when changing the active item. | |
enableHover | boolean | false | Enables or disables the hover effect. When enableHover is true, spacing between children cannot be added. |