Accordion
Animated expand/collapse with height: 0 → auto using AnimatePresence. Supports single or multiple open items.
Use and inspectUse this when the motion supports the surrounding content. Connect callbacks and state to your app, keep the reset preview as a local demonstration, and add a reduced-motion fallback for movement or looping. The preview is synthetic; it does not call a backend.Next: inspect the source below and replace the demo inputs with your own data.
AccordionAnimated expand/collapse with height: 0 → auto using AnimatePresence. Supports single or multiple open items.
"use client";
import { useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
type AccordionItem = { title: string; content: string };
type AccordionProps = {
items: AccordionItem[];
multiple?: boolean;
};
export function Accordion({ items, multiple = false }: AccordionProps) {
const [openIds, setOpenIds] = useState<Set<number>>(new Set());
function toggle(i: number) {
setOpenIds((prev) => {
const next = new Set(prev);
if (next.has(i)) {
next.delete(i);
} else {
if (!multiple) next.clear();
next.add(i);
}
return next;
});
}
return (
<div className="mp-accordion">
{items.map((item, i) => {
const isOpen = openIds.has(i);
return (
<div key={i} className="mp-accordion-item">
<button
className={"mp-accordion-trigger" + (isOpen ? " mp-accordion-trigger--open" : "")}
onClick={() => toggle(i)}
>
<span className="mp-accordion-title">{item.title}</span>
<motion.span
className="mp-accordion-icon"
animate={{ rotate: isOpen ? 45 : 0 }}
transition={{ duration: 0.18, ease: "easeOut" }}
>
+
</motion.span>
</button>
<AnimatePresence initial={false}>
{isOpen && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.22, ease: "easeInOut" }}
style={{ overflow: "hidden" }}
>
<div className="mp-accordion-content">{item.content}</div>
</motion.div>
)}
</AnimatePresence>
</div>
);
})}
</div>
);
}Install
npm install framer-motionProps
| Prop | Type | Default | Description |
|---|---|---|---|
| itemsrequired | { title: string; content: string }[] | — | Accordion items |
| multiple | boolean | false | Allow multiple items open at once |