- Published on
How to Manage Shadcn Accordion Open-close with useState in Next.js
- Authors
- Name
- Ripal & Zalak
How to Control Shadcn UI Accordion with React State in Next.js
Shadcn UI Accordion is a powerful component for creating collapsible sections. In this guide, we’ll explore how to control its open and close functionality using React’s useState
, making it responsive to changes in parent-controlled state.
Prerequisites
- Basic knowledge of React and Next.js.
- Familiarity with Shadcn UI library.
- Installed Shadcn UI components.
The Challenge
When using Shadcn UI Accordion, you might want to control which section is open based on a state variable from a parent component. This guide demonstrates how to achieve this.
Solution: Controlled Accordion
Here’s how you can implement a controlled Shadcn UI Accordion:
Code Implementation
import React, { useState, useEffect } from 'react'
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from '@/components/ui/accordion'
interface ChapterProps {
selectedChapter: number
setSelectedChapter: React.Dispatch<React.SetStateAction<number>>
chapters: Array<{
chaptername: string
chapterContent: Array<{ name: string; type: string; time: string }>
}>
}
const Chapters = ({ selectedChapter, setSelectedChapter, chapters }: ChapterProps) => {
const [openChapter, setOpenChapter] = useState<string>(`item-${selectedChapter}`)
// Sync local state with selectedChapter prop whenever it changes
useEffect(() => {
setOpenChapter(`item-${selectedChapter}`)
}, [selectedChapter])
return (
<div className="flex w-full flex-col space-y-4">
<Accordion
type="single"
collapsible
value={openChapter}
onValueChange={(value) => {
if (value) {
const chapterIndex = parseInt(value.replace('item-', ''), 10)
setSelectedChapter(chapterIndex)
}
setOpenChapter(value || '')
}}
className="w-full bg-white p-4"
>
{chapters.map((chapter, index) => (
<AccordionItem value={`item-${index}`} key={index}>
<AccordionTrigger>
<div className="flex w-full flex-col space-y-4 text-left">
<p className="mx-0 text-2xl font-semibold text-[#04445E]">{chapter.chaptername}</p>
</div>
</AccordionTrigger>
<AccordionContent>
{chapter.chapterContent.map((content, i) => (
<div key={i} className="flex flex-col space-y-6">
<p className="text-2xl text-[#011E29]">{content.name}</p>
<div className="flex space-x-4 text-[#011E29]">
<p>{content.type}</p>
<p>{content.time}</p>
</div>
</div>
))}
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}
export default Chapters
Key Features
Controlled State:
- The
Accordion
component usesvalue
to determine which item is open. - Updates the
openChapter
state and parentselectedChapter
when an item is clicked.
- The
Dynamic Synchronization:
- Automatically updates
openChapter
whenselectedChapter
changes in the parent.
- Automatically updates
Reusable Structure:
- The Accordion dynamically maps through chapters, making it adaptable for various datasets.
Common Issues and Fixes
Accordion Not Updating:
- Ensure the
value
prop matches the format ofuseState
.
- Ensure the
React Key Warnings:
- Use unique
key
values in lists, such asindex
or unique IDs.
- Use unique
State Sync Issues:
- Use
useEffect
to synchronize local and parent states.
- Use
Benefits
- Improved Usability: The Accordion responds to external state changes.
- Scalability: Supports dynamic content and API-driven data.
- Ease of Use: Simplifies state management with a single source of truth.
FAQs
How do I set a default open item?
Set the value
prop to the corresponding AccordionItem
value:
<Accordion value="item-0">...</Accordion>
Can I use this with API data?
Yes, fetch data in a useEffect
hook and pass it as chapters
:
useEffect(() => {
fetch('/api/chapters')
.then((res) => res.json())
.then((data) => setChapters(data))
}, [])
onValueChange
?
What is the role of It updates the open Accordion item and synchronizes with the parent component.