Accordion

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.

Installation

npm install motion
accordion.tsx
1
"use client"
2
import React, { ReactNode, ReactElement, isValidElement } from 'react';
3
import { AnimatePresence, motion } from 'motion/react';
4
import { ChevronDown } from 'lucide-react';
5
import { cn } from '@/lib/utils';
6
7
type AccordionContextType = {
8
isActive: boolean;
9
value: string;
10
onChangeIndex: (value: string) => void;
11
};
12
13
const AccordionContext = React.createContext<AccordionContextType>({
14
isActive: false,
15
value: '',
16
onChangeIndex: () => {}
17
});
18
19
const useAccordion = () => React.useContext(AccordionContext);
20
21
export function AccordionContainer({
22
children,
23
className,
24
}: {
25
children: ReactNode;
26
className?: string;
27
}) {
28
return (
29
<div className={cn('grid grid-cols-2 gap-1', className)}>{children}</div>
30
);
31
}
32
33
export function AccordionWrapper({
34
children
35
}: {
36
children: ReactNode
37
}) {
38
return <div>{children}</div>;
39
}
40
41
export function Accordion({
42
children,
43
multiple,
44
defaultValue,
45
}: {
46
children: ReactNode;
47
multiple?: boolean;
48
defaultValue?: string | string[];
49
}) {
50
const [activeIndex, setActiveIndex] = React.useState<string[]>(
51
multiple ? (defaultValue ? (Array.isArray(defaultValue) ? defaultValue : [defaultValue]) : []) :
52
(defaultValue ? (Array.isArray(defaultValue) ? [defaultValue[0]] : [defaultValue]) : [])
53
);
54
55
function onChangeIndex(value: string) {
56
setActiveIndex((currentActiveIndex) => {
57
if (!multiple) {
58
return value === currentActiveIndex[0] ? [] : [value];
59
}
60
61
if (currentActiveIndex.includes(value)) {
62
return currentActiveIndex.filter((i) => i !== value);
63
}
64
65
return [...currentActiveIndex, value];
66
});
67
}
68
69
return React.Children.map(children, (child) => {
70
if (!isValidElement<{ value?: string }>(child)) return null;
71
72
const value = child.props.value ?? '';
73
const isActive = multiple
74
? activeIndex.includes(value)
75
: activeIndex[0] === value;
76
77
return (
78
<AccordionContext.Provider value={{ isActive, value, onChangeIndex }}>
79
{React.cloneElement(child)}
80
</AccordionContext.Provider>
81
);
82
});
83
}
84
85
export function AccordionItem({
86
children,
87
value
88
}: {
89
children: ReactNode;
90
value: string
91
}) {
92
const { isActive } = useAccordion();
93
94
return (
95
<div
96
data-active={isActive || undefined}
97
className={`rounded-lg overflow-hidden mb-2 ${
98
isActive
99
? 'active border-2 dark:border-[#656fe2] border-[#F2F2F2] dark:bg-[#E0ECFB] bg-[#F2F2F2]'
100
: 'bg-transparent border-2 dark:hover:border-[#656fe2]'
101
}
102
`}
103
data-value={value}
104
>
105
{children}
106
</div>
107
);
108
}
109
110
export function AccordionHeader({
111
children,
112
customIcon,
113
className
114
}: {
115
children: ReactNode;
116
customIcon?: boolean;
117
className?: string;
118
}) {
119
const { isActive, value, onChangeIndex } = useAccordion();
120
121
return (
122
<motion.div
123
data-active={isActive || undefined}
124
className={`group p-4 cursor-pointer transition-all font-semibold dark:text-white text-black dark:hover:bg-[#1e2a78] hover:bg-[#F2F2F2] dark:hover:text-white hover:text-black flex justify-between items-center ${
125
isActive
126
? 'active dark:bg-[#1e2a78] bg-[#F2F2F2] '
127
: 'dark:bg-[#11112b] bg-white'
128
}
129
`}
130
onClick={() => onChangeIndex(value)}
131
>
132
{children}
133
{!customIcon && (
134
<ChevronDown
135
className={cn(
136
"transition-transform ",
137
isActive ? "rotate-180" : "rotate-0",
138
)}
139
/>
140
)}
141
</motion.div>
142
);
143
}
144
145
export function AccordionPanel({
146
children,
147
className
148
}: {
149
children: ReactNode;
150
className?: string;
151
}) {
152
const { isActive } = useAccordion();
153
154
return (
155
<AnimatePresence initial={true}>
156
{isActive && (
157
<motion.div
158
data-active={isActive || undefined}
159
initial={{ height: 0, overflow: 'hidden' }}
160
animate={{ height: 'auto', overflow: 'hidden' }}
161
exit={{ height: 0 }}
162
transition={{ type: 'spring', duration: 0.3, bounce: 0 }}
163
className={cn('group dark:bg-white bg-[#F2F2F2]', className)}
164
>
165
<motion.article
166
initial={{ clipPath: 'polygon(0 0, 100% 0, 100% 0, 0 0)' }}
167
animate={{ clipPath: 'polygon(0 0, 100% 0, 100% 100%, 0% 100%)' }}
168
exit={{
169
clipPath: 'polygon(0 0, 100% 0, 100% 0, 0 0)',
170
}}
171
transition={{
172
type: 'spring',
173
duration: 0.4,
174
bounce: 0,
175
}}
176
className={`p-3 bg-transparent text-black `}
177
>
178
{children}
179
</motion.article>
180
</motion.div>
181
)}
182
</AnimatePresence>
183
);
184
}

Structure

1
<Accordion defaultValue={['item-1']} multiple>
2
<AccordionItem value='item-1'>
3
<AccordionHeader></AccordionHeader>
4
<AccordionPanel></AccordionPanel>
5
</AccordionItem>
6
<AccordionItem value='item-2'>
7
<AccordionHeader></AccordionHeader>
8
<AccordionPanel></AccordionPanel>
9
</AccordionItem>
10
<AccordionItem value='item-3'>
11
<AccordionHeader></AccordionHeader>
12
<AccordionPanel></AccordionPanel>
13
</AccordionItem>
14
</Accordion>

Props

PropTypeDefaultDescription
defaultValuestringstring[]undefined
multiplebooleanfalseWhether the accordion allows multiple items to be active at the same time
childrenReactNode[]undefinedThe accordion items, including their headers and panels
classNamestring''Optional CSS class for styling the accordion wrapper

Example

Single Layout

What is a UI component?
A UI (User Interface) component is a modular, reusable element that serves a specific function within a graphical user interface. Examples include buttons, input fields, dropdown menus, sliders, and checkboxes.
Why are UI components important?
Key characteristics of UI components?

Multi Layout

What is a UI component?
Why are UI components important?
UI components promote consistency, efficiency, and scalability in software development. They allow developers to reuse code, maintain a consistent look and feel across an application, and easily make updates or modifications without affecting the entire system.
Key characteristics of UI components?