FluxUI Pro is live - modern UI, powerful animations, zero hassle.
Components
Interactive Accordion

Interactive Accordion

Smooth expanding panels with animated transitions, icons, and accessibility features.

Smooth Accordion

Expandable sections with staggered animations, rotating icons, and contextual actions.

Installation

1

Install the packages

npm i motion clsx tailwind-merge
2

Add util file

lib/util.ts
import { ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }
3

Copy and paste the following code into your project

interactive-accordion.tsx
"use client"; import { motion, AnimatePresence } from "motion/react"; import { useState } from "react"; import { cn } from "@/lib/utils"; import { FiChevronDown, FiChevronUp } from "react-icons/fi"; type AccordionItem = { id: string; title: string; content: string; icon?: React.ReactNode; }; type InteractiveAccordionProps = { items?: AccordionItem[]; allowMultiple?: boolean; }; const InteractiveAccordion = ({ items = [ { id: "1", title: "Getting Started", content: "Learn how to get started with our platform and set up your account.", icon: "🚀", }, { id: "2", title: "Features", content: "Explore all the amazing features we offer to enhance your experience.", icon: "✨", }, { id: "3", title: "Support", content: "Get help and support whenever you need it from our dedicated team.", icon: "💬", }, ], allowMultiple = false, }: InteractiveAccordionProps) => { const [openItems, setOpenItems] = useState<string[]>([]); const toggleItem = (id: string) => { if (allowMultiple) { setOpenItems(prev => prev.includes(id) ? prev.filter(item => item !== id) : [...prev, id] ); } else { setOpenItems(prev => prev.includes(id) ? [] : [id]); } }; const isOpen = (id: string) => openItems.includes(id); return ( <div className="w-full max-w-2xl mx-auto space-y-2"> {items.map((item, index) => ( <motion.div key={item.id} className="border border-neutral-200 dark:border-neutral-700 rounded-lg overflow-hidden" initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: index * 0.1, duration: 0.3 }} > <motion.button className="w-full px-6 py-4 text-left bg-white dark:bg-neutral-900 hover:bg-neutral-50 dark:hover:bg-neutral-800 transition-colors flex items-center justify-between" onClick={() => toggleItem(item.id)} whileTap={{ scale: 0.98 }} > <div className="flex items-center gap-3"> {item.icon && ( <motion.span className="text-lg" animate={{ rotate: isOpen(item.id) ? 360 : 0 }} transition={{ duration: 0.3 }} > {item.icon} </motion.span> )} <span className="font-medium text-neutral-900 dark:text-white"> {item.title} </span> </div> <motion.div animate={{ rotate: isOpen(item.id) ? 180 : 0 }} transition={{ duration: 0.3 }} > <FiChevronDown className="w-5 h-5 text-neutral-500" /> </motion.div> </motion.button> <AnimatePresence> {isOpen(item.id) && ( <motion.div initial={{ height: 0, opacity: 0 }} animate={{ height: "auto", opacity: 1 }} exit={{ height: 0, opacity: 0 }} transition={{ duration: 0.3, ease: "easeInOut" }} className="overflow-hidden" > <div className="px-6 pb-4 text-neutral-600 dark:text-neutral-400"> {item.content} </div> </motion.div> )} </AnimatePresence> </motion.div> ))} </div> ); }; export default InteractiveAccordion;
4

Update the import paths to match your project setup

Props

PropTypeDefaultDescription
itemsarray[]Array of accordion items with title and content.
allowMultiplebooleanfalseWhether multiple panels can be open at once.