Minimal Accordion
A quiet, accessible disclosure pattern for product and documentation pages.
html · tailwind · react
GUIDE_LOG // REACT_GUIDE
React is useful when FAQ content needs controlled disclosure state, filtering, or reuse across product surfaces.
React allows us to turn static FAQ sections into dynamic, stateful components that can be easily reused across multiple pages or projects.
A basic React accordion uses the useState hook to manage the active index.
import { useState } from "react";
interface FaqItem {
id: string;
question: string;
answer: string;
}
export function Accordion({ items }: { items: FaqItem[] }) {
const [openIndex, setOpenIndex] = useState<number | null>(null);
return (
<div className="space-y-4">
{items.map((item, index) => {
const triggerId = `faq-trigger-${item.id}`;
const panelId = `faq-panel-${item.id}`;
const isOpen = openIndex === index;
return (
<div key={item.id} className="border rounded-lg overflow-hidden">
<button
id={triggerId}
className="w-full text-left p-4 font-bold bg-gray-50 hover:bg-gray-100 transition-colors"
onClick={() => setOpenIndex(isOpen ? null : index)}
aria-expanded={isOpen}
aria-controls={panelId}
>
{item.question}
</button>
{isOpen && (
<div
id={panelId}
role="region"
aria-labelledby={triggerId}
className="p-4 border-t text-gray-600"
>
{item.answer}
</div>
)}
</div>
);
})}
</div>
);
}
For large FAQs, adding a client-side search filter significantly improves the user experience.
const [query, setQuery] = useState("");
const filteredItems = items.filter((item) =>
item.question.toLowerCase().includes(query.toLowerCase()),
);
return (
<>
<label htmlFor="faq-search">Search questions</label>
<input
id="faq-search"
type="search"
value={query}
onChange={(event) => setQuery(event.target.value)}
/>
<p aria-live="polite">{filteredItems.length} results</p>
{filteredItems.map((item) => (
<article key={item.id}>
<h3>{item.question}</h3>
<p>{item.answer}</p>
</article>
))}
</>
);
React makes it easy to manage ARIA attributes dynamically. Ensure you use:
aria-expanded: Set to true when the answer is visible.role="region": For the content container.id and aria-controls: To link the button to the content it reveals.Start with CSS transitions on opacity or grid rows. Keep the answer available to assistive technology only when it is visually open, and respect prefers-reduced-motion.
key prop, rather than the array index if the list can change.useMemo for your filtering logic.React allows for the creation of interactive and accessible FAQ components. By leveraging hooks and props, you can create a flexible system that adapts to your application's requirements.
A quiet, accessible disclosure pattern for product and documentation pages.
html · tailwind · react
A smooth, interactive accordion with elegant slide transitions and rotating icons.
html · tailwind · react
A scannable FAQ layout for landing pages with short, independent answers.
tailwind · react
Organize large sets of questions into logical groups for better discoverability.
html · tailwind · react
A powerful FAQ interface with a real-time search filter to help users find answers instantly.
tailwind · react
A standard FAQ layout paired with a prominent 'Contact Us' call-to-action for unresolved queries.
html · tailwind · react
A visually striking accordion designed specifically for dark-themed interfaces and high-contrast environments.
tailwind · react
A grid-based FAQ specifically tailored for handling billing, subscription, and pricing questions.
tailwind · react
A card-based FAQ layout designed to guide new users through their first steps and common setup hurdles.
tailwind · react
A minimalist, space-saving FAQ design for sidebars or mid-article content.
html · tailwind · react