The Accordion component is a collapsible content container that allows users to expand and collapse sections of content. It supports multiple variants and configurations to accommodate various design requirements.
import { useState } from 'react'import { Accordion } from '@dashflowx/core'const items = [{value: 'item-1',title: 'What is DashFlowX?',description: 'DashFlowX is a modern React component library designed for building beautiful and accessible user interfaces.'},{value: 'item-2',title: 'How do I get started?',description: 'You can install DashFlowX using npm or yarn and start building your application with our pre-built components.'}]export default function AccordionExample() {const [openValue, setOpenValue] = useState('item-1')return (<Accordiontype="single"collapsible={true}variant="basic"items={items}className="max-w-2xl mx-auto"value={openValue}onValueChange={setOpenValue}/>)}
This section combines both the code examples and live previews to show you how to implement accordions with proper state management and see them working in real-time.
import { useState } from 'react'import { Accordion } from '@dashflowx/core'function SingleAccordionExample() {const [openValue, setOpenValue] = useState('item-1')const handleValueChange = (value: string) => {setOpenValue(value)console.log('Accordion opened:', value)}const items = [{value: 'item-1',title: 'What is DashFlowX?',description: 'DashFlowX is a modern React component library designed for building beautiful and accessible user interfaces. It provides a comprehensive set of pre-built components that follow modern design principles and accessibility standards.'},{value: 'item-2',title: 'How do I get started?',description: 'You can install DashFlowX using npm or yarn and start building your application with our pre-built components. The library includes TypeScript support and extensive documentation to help you get up and running quickly.'},{value: 'item-3',title: 'What features does it include?',description: 'DashFlowX includes responsive design, dark mode support, accessibility features, and a flexible theming system. All components are built with performance and developer experience in mind.'}]return (<div className="max-w-2xl mx-auto"><div className="mb-4 p-4 bg-blue-50 rounded-lg"><p className="text-sm text-blue-800"><strong>Current open item:</strong> {openValue}</p></div><Accordiontype="single"collapsible={true}variant="basic"items={items}value={openValue}onValueChange={handleValueChange}className="space-y-2"/></div>)}
Live Preview:
import { useState } from 'react'import { Accordion } from '@dashflowx/core'function MultipleAccordionExample() {const [openValues, setOpenValues] = useState(['item-1'])const handleValueChange = (values: string[]) => {setOpenValues(values)console.log('Open items:', values)}const items = [{value: 'item-1',title: 'Installation Guide',description: 'Learn how to install DashFlowX in your project using npm, yarn, or pnpm. We provide step-by-step instructions for different project setups.'},{value: 'item-2',title: 'Getting Started',description: 'Follow our quick start guide to create your first component and understand the basic concepts of DashFlowX.'},{value: 'item-3',title: 'Component Examples',description: 'Explore our comprehensive collection of component examples with different variants, sizes, and configurations.'}]return (<div className="max-w-2xl mx-auto"><div className="mb-4 p-4 bg-green-50 rounded-lg"><p className="text-sm text-green-800"><strong>Open items:</strong> {openValues.join(', ') || 'None'}</p><p className="text-sm text-green-700 mt-1"><strong>Total open:</strong> {openValues.length}</p></div><Accordiontype="multiple"collapsible={true}variant="basic"items={items}value={openValues}onValueChange={handleValueChange}className="space-y-2"/></div>)}
Live Preview:
import { useState, useEffect } from 'react'import { Accordion } from '@dashflowx/core'function AdvancedAccordionExample() {const [openValue, setOpenValue] = useState('item-1')const [lastOpened, setLastOpened] = useState<string | null>(null)const [openCount, setOpenCount] = useState(0)const handleValueChange = (value: string) => {const previousValue = openValuesetOpenValue(value)setLastOpened(previousValue)setOpenCount(prev => prev + 1)// Custom analytics trackingif (typeof window !== 'undefined' && window.gtag) {window.gtag('event', 'accordion_item_open', {item_value: value,previous_value: previousValue,open_count: openCount + 1})}// Custom business logicif (value === 'installation') {console.log('User opened installation guide - showing related content')}}const items = [{value: 'installation',title: 'Installation Guide',description: 'Complete installation guide with step-by-step instructions for different environments and package managers.'},{value: 'configuration',title: 'Configuration',description: 'Learn how to configure DashFlowX for your project, including theming, plugins, and custom settings.'},{value: 'examples',title: 'Examples & Demos',description: 'Explore real-world examples and interactive demos to see DashFlowX components in action.'}]return (<div className="max-w-2xl mx-auto"><div className="mb-4 p-4 bg-purple-50 rounded-lg"><div className="grid grid-cols-2 gap-4 text-sm"><div><p className="text-purple-800 font-medium">Current Open:</p><p className="text-purple-700">{openValue}</p></div><div><p className="text-purple-800 font-medium">Last Opened:</p><p className="text-purple-700">{lastOpened || 'None'}</p></div><div><p className="text-purple-800 font-medium">Total Opens:</p><p className="text-purple-700">{openCount}</p></div><div><p className="text-purple-800 font-medium">Status:</p><p className="text-purple-700">{openValue ? 'Active' : 'Closed'}</p></div></div></div><Accordiontype="single"collapsible={true}variant="basic"items={items}value={openValue}onValueChange={handleValueChange}className="space-y-2"/></div>)}
Live Preview:
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'basic' | 'one' | - | The visual style variant of the accordion |
type | 'single' | 'multiple' | 'single' | Whether only one or multiple items can be open |
collapsible | boolean | true | Whether items can be collapsed when clicked again |
items | Array<iDfxAccordionItems> | - | Array of accordion items |
defaultValue | string | - | The default open item value |
value | string | - | Controlled value for the open item |
onValueChange | (value: string) => void | - | Callback when the open item changes |
disabled | boolean | false | Whether the entire accordion is disabled |
className | string | - | Additional CSS classes for the accordion container |
itemClassName | string | - | Additional CSS classes for each accordion item |
itemTriggerClassName | string | - | Additional CSS classes for each accordion trigger |
itemContentClassName | string | - | Additional CSS classes for each accordion content |
onClick | (event: React.MouseEvent) => void | - | Callback when accordion container is clicked |
interface iDfxAccordionItems {value: string; // Unique identifier for the itemtitle: string | JSX.Element; // The title/trigger contentdescription: string | JSX.Element; // The content to display when expandeddisabled?: boolean; // Whether this specific item is disabled}
// ✅ Controlled (recommended for most cases)const [openValue, setOpenValue] = useState('item-1')<Accordionvalue={openValue}onValueChange={setOpenValue}// ... other props/>// ✅ Uncontrolled (simple cases)<AccordiondefaultValue="item-1"// ... other props/>
import { useState, useEffect } from 'react'function PersistentAccordion() {const [openValue, setOpenValue] = useState(() => {// Load from localStorage on mountif (typeof window !== 'undefined') {return localStorage.getItem('accordion-open') || 'item-1'}return 'item-1'})useEffect(() => {// Save to localStorage on changelocalStorage.setItem('accordion-open', openValue)}, [openValue])return (<Accordionvalue={openValue}onValueChange={setOpenValue}// ... other props/>)}
function ComplexAccordion() {const [state, setState] = useState({openValue: 'item-1',lastOpened: null,openCount: 0,userPreferences: {}})const handleValueChange = (value: string) => {setState(prev => ({...prev,openValue: value,lastOpened: prev.openValue,openCount: prev.openCount + 1}))// Additional side effectstrackAnalytics(value)updateUserPreferences(value)}return (<Accordionvalue={state.openValue}onValueChange={handleValueChange}// ... other props/>)}
// ✅ Good: Use proper ARIA attributes<Accordiontype="single"aria-label="FAQ Section"aria-describedby="faq-description"// ... other props/>// ✅ Good: Provide descriptive labelsconst items = [{value: 'installation',title: 'Installation Guide',description: 'Step-by-step installation instructions for DashFlowX'}]
// ✅ Good: Ensure keyboard accessibilityfunction AccessibleAccordion() {const handleKeyDown = (event: React.KeyboardEvent) => {if (event.key === 'Enter' || event.key === ' ') {event.preventDefault()// Handle accordion toggle}}return (<AccordiononKeyDown={handleKeyDown}// ... other props/>)}
// ✅ Good: Provide context for screen readers<div><h2 id="accordion-heading">Frequently Asked Questions</h2><p id="accordion-description">Expand each section to learn more about DashFlowX</p><Accordionaria-labelledby="accordion-heading"aria-describedby="accordion-description"// ... other props/></div>
import { useMemo } from 'react'function OptimizedAccordion() {const expensiveContent = useMemo(() => {// Expensive computation or renderingreturn <ComplexComponent data={largeDataset} />}, [largeDataset])const items = [{value: 'complex',title: 'Complex Content',description: expensiveContent}]return <Accordion items={items} />}
import { lazy, Suspense } from 'react'const LazyComponent = lazy(() => import('./HeavyComponent'))function LazyAccordion() {const items = [{value: 'lazy',title: 'Heavy Component',description: (<Suspense fallback={<div>Loading...</div>}><LazyComponent /></Suspense>)}]return <Accordion items={items} />}
import { useCallback } from 'react'import { debounce } from 'lodash'function DebouncedAccordion() {const debouncedValueChange = useCallback(debounce((value: string) => {// Expensive operation (API call, analytics, etc.)trackUserBehavior(value)}, 300),[])return (<AccordiononValueChange={debouncedValueChange}// ... other props/>)}
// ✅ Good: Use consistent design tokensconst accordionStyles = {container: 'max-w-4xl mx-auto space-y-6',item: 'border border-gray-200 rounded-lg overflow-hidden',trigger: 'px-6 py-4 text-left font-medium text-gray-900 hover:bg-gray-50',content: 'px-6 py-4 bg-gray-50 text-gray-700'}<AccordionclassName={accordionStyles.container}itemClassName={accordionStyles.item}itemTriggerClassName={accordionStyles.trigger}itemContentClassName={accordionStyles.content}// ... other props/>
// ✅ Good: Provide clear visual feedbackconst getItemStyles = (isOpen: boolean, isDisabled: boolean) => {if (isDisabled) return 'opacity-50 cursor-not-allowed'if (isOpen) return 'bg-blue-50 border-blue-200'return 'hover:bg-gray-50 transition-colors duration-200'}<AccordionitemClassName={getItemStyles(isOpen, isDisabled)}// ... other props/>
// ✅ Good: Ensure mobile-friendly designfunction ResponsiveAccordion() {const isMobile = useMediaQuery('(max-width: 768px)')return (<AccordionclassName={isMobile ? 'space-y-2' : 'space-y-4'}itemTriggerClassName={isMobile ? 'px-4 py-3' : 'px-6 py-4'}// ... other props/>)}
import { ErrorBoundary } from 'react-error-boundary'function ErrorFallback({ error }: { error: Error }) {return (<div className="p-4 bg-red-50 border border-red-200 rounded-lg"><p className="text-red-800">Something went wrong: {error.message}</p></div>)}function SafeAccordion() {return (<ErrorBoundary FallbackComponent={ErrorFallback}><Accordion// ... props/></ErrorBoundary>)}
// ✅ Good: Use strict typinginterface AccordionItem {value: stringtitle: stringdescription: stringdisabled?: booleanmetadata?: Record<string, unknown>}interface AccordionProps {items: AccordionItem[]type: 'single' | 'multiple'value?: string | string[]onValueChange?: (value: string | string[]) => void}function TypedAccordion({ items, type, value, onValueChange }: AccordionProps) {return (<Accordionitems={items}type={type}value={value}onValueChange={onValueChange}/>)}
// ✅ Good: Test accordion behaviorimport { render, screen, fireEvent } from '@testing-library/react'test('accordion toggles correctly', () => {render(<AccordionExample />)const trigger = screen.getByText('What is DashFlowX?')fireEvent.click(trigger)expect(screen.getByText('DashFlowX is a modern React component library')).toBeVisible()})test('single type closes other items', () => {render(<SingleTypeAccordion />)const firstItem = screen.getByText('Item 1')const secondItem = screen.getByText('Item 2')fireEvent.click(firstItem)fireEvent.click(secondItem)// First item should be closed when second is openedexpect(screen.queryByText('Content 1')).not.toBeVisible()})
// ✅ Good: Group related items logicallyconst faqItems = [{value: 'getting-started',title: 'Getting Started',description: 'Basic setup and installation'},{value: 'installation',title: 'Installation',description: 'Detailed installation steps'},{value: 'configuration',title: 'Configuration',description: 'Setting up your project'}]const advancedItems = [{value: 'customization',title: 'Customization',description: 'Advanced customization options'},{value: 'performance',title: 'Performance',description: 'Optimization techniques'}]
// ✅ Good: Use accordions for progressive disclosurefunction ProgressiveAccordion() {const [userLevel, setUserLevel] = useState<'beginner' | 'intermediate' | 'advanced'>('beginner')const getItemsForLevel = (level: string) => {switch (level) {case 'beginner':return basicItemscase 'intermediate':return [...basicItems, ...intermediateItems]case 'advanced':return [...basicItems, ...intermediateItems, ...advancedItems]default:return basicItems}}return (<div><select value={userLevel} onChange={(e) => setUserLevel(e.target.value as any)}><option value="beginner">Beginner</option><option value="intermediate">Intermediate</option><option value="advanced">Advanced</option></select><Accordionitems={getItemsForLevel(userLevel)}// ... other props/></div>)}
// ✅ Good: Validate accordion contentfunction validateAccordionItems(items: AccordionItem[]): boolean {return items.every(item => {if (!item.value || !item.title || !item.description) {console.error('Invalid accordion item:', item)return false}if (item.value.length < 3) {console.warn('Short accordion value:', item.value)}return true})}function ValidatedAccordion({ items }: { items: AccordionItem[] }) {useEffect(() => {if (!validateAccordionItems(items)) {console.error('Invalid accordion items detected')}}, [items])return <Accordion items={items} />}
// ✅ Good: Add structured data for SEOfunction SEOAccordion() {const structuredData = {"@context": "https://schema.org","@type": "FAQPage","mainEntity": items.map(item => ({"@type": "Question","name": item.title,"acceptedAnswer": {"@type": "Answer","text": item.description}}))}return (<><scripttype="application/ld+json"dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}/><Accordion items={items} /></>)}
// ✅ Good: Track user interactionsfunction AnalyticsAccordion() {const trackAccordionOpen = (itemValue: string, itemTitle: string) => {// Google Analyticsif (typeof window !== 'undefined' && window.gtag) {window.gtag('event', 'accordion_open', {event_category: 'User Interaction',event_label: itemTitle,value: itemValue})}// Custom analyticsanalytics.track('accordion_item_opened', {item: itemValue,title: itemTitle,timestamp: Date.now()})}return (<AccordiononValueChange={trackAccordionOpen}// ... other props/>)}
These best practices ensure your accordion components are accessible, performant, maintainable, and provide an excellent user experience across all devices and use cases.
