Accordion
accordionuiVertically stacked, collapsible panels — one or many open at a time. Use for FAQs, settings groups, and any place where space is tight but content needs to stay browsable. Built on Radix UI with smooth animation and full keyboard support.
Installation
$pnpm dlx shadcn@latest add https://uipkge.dev/r/react/accordion.json$npx shadcn@latest add https://uipkge.dev/r/react/accordion.json$yarn dlx shadcn@latest add https://uipkge.dev/r/react/accordion.json$bunx shadcn@latest add https://uipkge.dev/r/react/accordion.jsonNamed registry:
npx shadcn@latest add @uipkge-react/accordionInstalls to:components/ui/accordion/Examples
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
variant | | default | optional |
npm dependencies
Used by
Files installed (3)
components/ui/accordion/accordion.tsx4.1 kB
'use client' import * as React from 'react' import * as AccordionPrimitive from '@radix-ui/react-accordion' import { ChevronDown } from 'lucide-react' import { cn } from '@/lib/utils' import { accordionVariants, accordionItemVariants, accordionTriggerVariants } from './accordion.variants' type AccordionVariant = 'default' | 'separated' | 'ghost' // The Vue source threads the chosen variant from <Accordion> down to each // <AccordionItem>/<AccordionTrigger> via provide/inject. React's equivalent is // context — the Root sets it, Item/Trigger read it (default 'default'). const AccordionVariantContext = React.createContext<AccordionVariant>('default') const Accordion = React.forwardRef< React.ElementRef<typeof AccordionPrimitive.Root>, React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Root> & { variant?: AccordionVariant } >(({ className, variant = 'default', ...props }, ref) => ( <AccordionVariantContext.Provider value={variant}> <AccordionPrimitive.Root ref={ref} data-uipkge="" data-slot="accordion" data-variant={variant} className={cn(accordionVariants({ variant }), className)} {...props} /> </AccordionVariantContext.Provider> )) Accordion.displayName = 'Accordion' const AccordionItem = React.forwardRef< React.ElementRef<typeof AccordionPrimitive.Item>, React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item> >(({ className, ...props }, ref) => { const variant = React.useContext(AccordionVariantContext) return ( <AccordionPrimitive.Item ref={ref} data-uipkge="" data-slot="accordion-item" className={cn(accordionItemVariants({ variant }), className)} {...props} /> ) }) AccordionItem.displayName = 'AccordionItem' const AccordionHeader = React.forwardRef< React.ElementRef<typeof AccordionPrimitive.Header>, React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Header> >(({ className, ...props }, ref) => ( <AccordionPrimitive.Header ref={ref} data-uipkge="" data-slot="accordion-header" className={cn('flex', className)} {...props} /> )) AccordionHeader.displayName = 'AccordionHeader' const AccordionTrigger = React.forwardRef< React.ElementRef<typeof AccordionPrimitive.Trigger>, React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger> >(({ className, children, ...props }, ref) => { const variant = React.useContext(AccordionVariantContext) // Do not wrap Header here — matches Vue/Reka and lets consumers use // <AccordionHeader> for custom layouts (e.g. price outside the trigger). return ( <AccordionPrimitive.Trigger ref={ref} data-uipkge="" data-slot="accordion-trigger" className={cn(accordionTriggerVariants({ variant }), className)} {...props} > {children} <ChevronDown className="text-muted-foreground size-4 shrink-0 transition-transform duration-200 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-[state=open]/accordion-trigger:rotate-180 motion-reduce:transition-none" aria-hidden="true" /> </AccordionPrimitive.Trigger> ) }) AccordionTrigger.displayName = 'AccordionTrigger' const AccordionContent = React.forwardRef< React.ElementRef<typeof AccordionPrimitive.Content>, React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content> >(({ className, children, ...props }, ref) => ( <AccordionPrimitive.Content ref={ref} data-uipkge="" data-slot="accordion-content" className={cn( 'text-muted-foreground overflow-hidden text-sm', // Height via Radix CSS vars + tw-animate-css (not height:auto). // duration/ease set --tw-duration/--tw-ease consumed by the utility. 'data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down', 'duration-200 ease-[cubic-bezier(0.32,0.72,0,1)]', 'motion-reduce:animate-none', className, )} {...props} > <div className="pt-0 pb-4">{children}</div> </AccordionPrimitive.Content> )) AccordionContent.displayName = 'AccordionContent' export { Accordion, AccordionItem, AccordionHeader, AccordionTrigger, AccordionContent }components/ui/accordion/accordion.variants.ts1.8 kB
import type { VariantProps } from 'class-variance-authority' import { cva } from 'class-variance-authority' /** * Variant definitions live in their own file (rather than the package * `index.ts`) so consuming Vue SFCs can import without creating a circular * dependency through the index. See card.variants.ts for the canonical * example + the SSR symptom that motivated the split. */ export const accordionVariants = cva('w-full', { variants: { variant: { // Bottom-border between items (the classic shadcn look). default: '', // Each item is its own bordered card with a small gap between them. separated: 'space-y-2', // Borderless. Use when the parent container already provides framing. ghost: '', }, }, defaultVariants: { variant: 'default', }, }) export const accordionItemVariants = cva('', { variants: { variant: { default: 'border-b border-border last:border-b-0', separated: 'rounded-md border border-border bg-card overflow-hidden', ghost: '', }, }, defaultVariants: { variant: 'default', }, }) export const accordionTriggerVariants = cva( 'group/accordion-trigger flex w-full items-center justify-between gap-3 text-sm font-medium text-foreground outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring/40 hover:text-primary disabled:pointer-events-none disabled:opacity-50', { variants: { variant: { default: 'py-4', separated: 'px-4 py-3 hover:bg-muted/50', ghost: 'py-3', }, }, defaultVariants: { variant: 'default', }, }, ) export type AccordionVariants = VariantProps<typeof accordionVariants> export type AccordionItemVariants = VariantProps<typeof accordionItemVariants> export type AccordionTriggerVariants = VariantProps<typeof accordionTriggerVariants>components/ui/accordion/index.ts0.4 kB
export { Accordion, AccordionItem, AccordionHeader, AccordionTrigger, AccordionContent } from './accordion' // Re-export variant API from the sibling file (kept separate to avoid an // import cycle and to give consumers a stable `accordionVariants` path). export { accordionVariants, accordionItemVariants, accordionTriggerVariants, type AccordionVariants, type AccordionItemVariants, type AccordionTriggerVariants, } from './accordion.variants'
Raw manifest:https://uipkge.dev/r/react/accordion.json