motion-primitives
components
ui
animation
motion

animated-group

A wrapper that adds animated transitions to a group of child elements.

animated
select
transition
View Docs

Source Code

Files
animated-group.tsx
1'use client';
2import { ReactNode } from 'react';
3import { motion, Variants } from 'motion/react';
4import React from 'react';
5
6export type PresetType =
7  | 'fade'
8  | 'slide'
9  | 'scale'
10  | 'blur'
11  | 'blur-slide'
12  | 'zoom'
13  | 'flip'
14  | 'bounce'
15  | 'rotate'
16  | 'swing';
17
18export type AnimatedGroupProps = {
19  children: ReactNode;
20  className?: string;
21  variants?: {
22    container?: Variants;
23    item?: Variants;
24  };
25  preset?: PresetType;
26  as?: React.ElementType;
27  asChild?: React.ElementType;
28};
29
30const defaultContainerVariants: Variants = {
31  visible: {
32    transition: {
33      staggerChildren: 0.1,
34    },
35  },
36};
37
38const defaultItemVariants: Variants = {
39  hidden: { opacity: 0 },
40  visible: { opacity: 1 },
41};
42
43const presetVariants: Record<
44  PresetType,
45  Variants
46> = {
47  fade: {
48  },
49  slide: {
50    hidden: { y: 20 },
51    visible: { y: 0 },
52  },
53  scale: {
54    hidden: { scale: 0.8 },
55    visible: { scale: 1 },
56  },
57  blur: {
58    hidden: { filter: 'blur(4px)' },
59    visible: { filter: 'blur(0px)' },
60  },
61  'blur-slide': {
62    hidden: { filter: 'blur(4px)', y: 20 },
63    visible: { filter: 'blur(0px)', y: 0 },
64  },
65  zoom: {
66    hidden: { scale: 0.5 },
67    visible: {
68      scale: 1,
69      transition: { type: 'spring', stiffness: 300, damping: 20 },
70    },
71  },
72  flip: {
73    hidden: { rotateX: -90 },
74    visible: {
75      rotateX: 0,
76      transition: { type: 'spring', stiffness: 300, damping: 20 },
77    },
78  },
79  bounce: {
80    hidden: { y: -50 },
81    visible: {
82      y: 0,
83      transition: { type: 'spring', stiffness: 400, damping: 10 },
84    },
85  },
86  rotate: {
87    hidden: { rotate: -180 },
88    visible: {
89      rotate: 0,
90      transition: { type: 'spring', stiffness: 200, damping: 15 },
91    },
92  },
93  swing: {
94    hidden: { rotate: -10 },
95    visible: {
96      rotate: 0,
97      transition: { type: 'spring', stiffness: 300, damping: 8 },
98    },
99  },
100};
101
102const addDefaultVariants = (variants: Variants) => ({
103    hidden: { ...defaultItemVariants.hidden, ...variants.hidden },
104    visible: { ...defaultItemVariants.visible, ...variants.visible },
105});
106
107function AnimatedGroup({
108  children,
109  className,
110  variants,
111  preset,
112  as = 'div',
113  asChild = 'div',
114}: AnimatedGroupProps) {
115  const selectedVariants = {
116    item: addDefaultVariants(preset ? presetVariants[preset] : {}),
117    container: addDefaultVariants(defaultContainerVariants)
118  };
119  const containerVariants = variants?.container || selectedVariants.container;
120  const itemVariants = variants?.item || selectedVariants.item;
121
122  const MotionComponent = motion.create(as as keyof JSX.IntrinsicElements);
123
124  const MotionChild = motion.create(asChild as keyof JSX.IntrinsicElements);
125
126  return (
127    <MotionComponent
128      initial='hidden'
129      animate='visible'
130      variants={containerVariants}
131      className={className}
132    >
133      {React.Children.map(children, (child, index) => (
134        <MotionChild key={index} variants={itemVariants}>
135          {child}
136        </MotionChild>
137      ))}
138    </MotionComponent>
139  );
140}
141
142export { AnimatedGroup };
143