Accordion
Component
Tailwind CSS accordion components to concisely show and hide content. Perfect for FAQs, nested navigation, and building a flexible flex accordion interface.
On this page
Build clean, accessible Tailwind CSS accordion components to elegantly manage vertical space. Whether you need a simple flat design, a bordered Tailwind accordion component, or a seamless ghost variant, these flex accordion layouts are ready to drop into your Next.js and React projects.
Looking for other ways to display structured content? Check out our Card or Modal components.
Basic Accordion
A flat, clean accordion perfect for minimalist interfaces.
"use client";
import React, { useState, useRef, useEffect } from "react";
const AnimatedArrow: React.FC<{ isExpanded: boolean }> = ({ isExpanded }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
className={`h-5 w-5 transform transition-transform duration-300 ease-in-out text-neutral-400 dark:text-neutral-500 ${
isExpanded ? "rotate-180" : ""
}`}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M19 9l-7 7-7-7"
/>
</svg>
);
const BasicAccordion: React.FC = () => {
const [isExpanded, setIsExpanded] = useState(false);
const [height, setHeight] = useState<number | undefined>(0);
const contentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isExpanded) {
const contentEl = contentRef.current;
setHeight(contentEl?.scrollHeight);
} else {
setHeight(0);
}
}, [isExpanded]);
const toggleAccordion = () => setIsExpanded(!isExpanded);
return (
<div className="max-w-lg mx-auto sm:max-w-sm lg:max-w-3xl my-4">
<div className="rounded-2xl overflow-hidden bg-neutral-50 dark:bg-neutral-900 transition-colors duration-300 border border-transparent dark:border-white/5 shadow-sm">
<div
className="flex items-center justify-between cursor-pointer py-4 px-6 transition-colors duration-200 hover:bg-neutral-100 dark:hover:bg-neutral-800"
onClick={toggleAccordion}
>
<div className="text-[15px] font-medium text-neutral-900 dark:text-neutral-100">
What is your return policy?
</div>
<span className="ml-6 flex items-center">
<AnimatedArrow isExpanded={isExpanded} />
</span>
</div>
<div
className="transition-all duration-300 ease-in-out overflow-hidden"
style={{ height }}
>
<div ref={contentRef} className="px-6 pb-5 pt-1">
<p className="text-[14px] leading-relaxed text-neutral-600 dark:text-neutral-400">
We offer a 30-day money-back guarantee on all our products. If you are not completely satisfied, you can return your purchase for a full refund or exchange it for another item.
</p>
</div>
</div>
</div>
</div>
);
};
export default BasicAccordion;
Bordered Accordion
Bordered accordion that elegantly delineates content.
"use client";
import React, { useState, useRef, useEffect } from "react";
const AnimatedArrow: React.FC<{ isExpanded: boolean }> = ({ isExpanded }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
className={`h-5 w-5 transform transition-transform duration-300 ease-in-out text-neutral-400 dark:text-neutral-500 ${
isExpanded ? "rotate-180" : ""
}`}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M19 9l-7 7-7-7"
/>
</svg>
);
const BorderedAccordion: React.FC = () => {
const [isExpanded, setIsExpanded] = useState(false);
const [height, setHeight] = useState<number | undefined>(0);
const contentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isExpanded) {
const contentEl = contentRef.current;
setHeight(contentEl?.scrollHeight);
} else {
setHeight(0);
}
}, [isExpanded]);
const toggleAccordion = () => setIsExpanded(!isExpanded);
return (
<div className="max-w-lg mx-auto sm:max-w-sm lg:max-w-3xl my-4">
<div className="border border-neutral-200 dark:border-neutral-800 rounded-2xl overflow-hidden bg-white dark:bg-black transition-colors duration-300">
<div
className={`flex items-center justify-between cursor-pointer py-4 px-6 transition-colors duration-200 hover:bg-neutral-50 dark:hover:bg-neutral-900/50 ${
isExpanded ? "border-b border-neutral-100 dark:border-neutral-800/50" : ""
}`}
onClick={toggleAccordion}
>
<div className="text-[15px] font-medium text-neutral-900 dark:text-neutral-100">
How long does shipping take?
</div>
<span className="ml-6 flex items-center">
<AnimatedArrow isExpanded={isExpanded} />
</span>
</div>
<div
className="transition-all duration-300 ease-in-out overflow-hidden bg-white dark:bg-black"
style={{ height }}
>
<div ref={contentRef} className="px-6 py-4">
<p className="text-[14px] leading-relaxed text-neutral-600 dark:text-neutral-400">
Standard shipping usually takes 3-5 business days within the continental US. Expedited shipping options are available at checkout for an additional fee.
</p>
</div>
</div>
</div>
</div>
);
};
export default BorderedAccordion;
Ghost Accordion
A minimal, borderless accordion that floats on the page. Ideal for dense informational sections.
"use client";
import React, { useState, useRef, useEffect } from "react";
const AnimatedArrow: React.FC<{ isExpanded: boolean }> = ({ isExpanded }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
className={`h-5 w-5 transform transition-transform duration-300 ease-in-out text-neutral-400 dark:text-neutral-500 ${
isExpanded ? "rotate-180" : ""
}`}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M19 9l-7 7-7-7"
/>
</svg>
);
const GhostAccordion: React.FC = () => {
const [isExpanded, setIsExpanded] = useState(false);
const [height, setHeight] = useState<number | undefined>(0);
const contentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isExpanded) {
const contentEl = contentRef.current;
setHeight(contentEl?.scrollHeight);
} else {
setHeight(0);
}
}, [isExpanded]);
const toggleAccordion = () => setIsExpanded(!isExpanded);
return (
<div className="max-w-lg mx-auto sm:max-w-sm lg:max-w-3xl my-4 group">
<div className="rounded-2xl overflow-hidden bg-transparent transition-colors duration-300">
<div
className="flex items-center justify-between cursor-pointer py-4 px-4 transition-colors duration-200 hover:bg-neutral-100 dark:hover:bg-neutral-800/50 rounded-xl"
onClick={toggleAccordion}
>
<div className="text-[15px] font-medium text-neutral-900 dark:text-neutral-100">
Do you ship internationally?
</div>
<span className="ml-6 flex items-center">
<AnimatedArrow isExpanded={isExpanded} />
</span>
</div>
<div
className="transition-all duration-300 ease-in-out overflow-hidden"
style={{ height }}
>
<div ref={contentRef} className="px-4 pb-4 pt-2">
<p className="text-[14px] leading-relaxed text-neutral-600 dark:text-neutral-400">
Yes, we ship to over 50 countries worldwide. International shipping rates and delivery times vary by location. Please review our shipping policy for more details.
</p>
</div>
</div>
</div>
</div>
);
};
export default GhostAccordion;