Accordion
nexvyn/accordionFreeComponent
A vertically stacked set of interactive headings that each reveal a section of content, with a grid-rows unfold animation and full keyboard support.
Live preview
live · rendered by the registry
Rendered by nexvyn on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://ui.nexvyn.dev/r/accordion.jsonSource
1'use client'23import {4 createContext,5 forwardRef,6 useCallback,7 useContext,8 useId,9 useMemo,10 useState,11 type HTMLAttributes,12 type ReactNode,13} from 'react'14import { useReducedMotion } from 'motion/react'15import { cn } from '@/lib/utils'1617interface AccordionContextValue {18 type: 'single' | 'multiple'19 collapsible: boolean20 expandedValues: string[]21 toggle: (value: string) => void22 reducedMotion: boolean23}2425const AccordionContext = createContext<AccordionContextValue | null>(null)2627function useAccordionCtx(componentName: string) {28 const ctx = useContext(AccordionContext)29 if (!ctx) throw new Error(`${componentName} must be used within <Accordion>`)30 return ctx31}3233interface AccordionItemContextValue {34 value: string35 isExpanded: boolean36 disabled: boolean37 triggerId: string38 contentId: string39}4041const AccordionItemContext = createContext<AccordionItemContextValue | null>(null)4243function useAccordionItemCtx(componentName: string) {44 const ctx = useContext(AccordionItemContext)45 if (!ctx) throw new Error(`${componentName} must be used within <AccordionItem>`)46 return ctx47}4849export interface AccordionProps extends Omit<HTMLAttributes<HTMLDivElement>, 'defaultValue'> {50 type: 'single' | 'multiple'51 collapsible?: boolean52 value?: string | string[]53 defaultValue?: string | string[]54 onValueChange?: (value: string | string[]) => void55 className?: string56 children?: ReactNode57}5859export const Accordion = forwardRef<HTMLDivElement, AccordionProps>(60 (61 {62 type,63 collapsible = false,64 value: valueProp,65 defaultValue,66 onValueChange,67 className,68 children,69 ...props70 },71 ref,72 ) => {73 const isControlled = valueProp !== undefined74 const reducedMotion = useReducedMotion() ?? false7576 const normalizeValue = useCallback((v: string | string[] | undefined): string[] => {77 if (v === undefined) return []78 if (Array.isArray(v)) return v79 return v ? [v] : []80 }, [])8182 const [internal, setInternal] = useState<string[]>(() => normalizeValue(defaultValue))83 const expandedValues = isControlled ? normalizeValue(valueProp) : internal8485 const toggle = useCallback(86 (itemValue: string) => {87 const isExpanded = expandedValues.includes(itemValue)8889 let next: string[]9091 if (type === 'single') {92 if (isExpanded) {93 next = collapsible ? [] : expandedValues94 } else {95 next = [itemValue]96 }97// … truncated
What it pulls in
npm packages
Files it writes
More from nexvyn
All 40 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Action Buttonaction-button | nexvyn | Components | Free | 3 deps · 4 files | |
| Adaptive Actionsadaptive-actions | nexvyn | Components | Free | 1 dep | |
| AI Inputai-input | nexvyn | Components | Free | 1 dep · 2 files | |
| Badgebadge | nexvyn | Components | Free | 2 deps · 2 files | |
| Bars Themebars-theme | nexvyn | Components | Free | 1 dep · 3 files | |
| Bounce Sidebarbounce-sidebar | nexvyn | Components | Free | 1 dep · 2 files | |
| Breadcrumbsbreadcrumbs | nexvyn | Components | Free | 1 dep · 2 files | |
| Buttonbutton | nexvyn | Components | Free | 3 deps · 3 files |
