shadimage
kanpeki
uiaccordionsource: ok · refreshed

Accordion

npx shadcn add "https://kanpeki.vercel.app/r/accordion.json"
free

Interaction

accordion

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
iconslucide-react

Registry dependencies

@kanpeki/collapsible

Files4 · source cached 10/8/2026, 4:09:06 AM

src/registry/ui/accordion/index.ts
export * from "./accordion";
export * as Accordion from "./namespace";
src/registry/ui/accordion/accordion.tsx
"use client";

import { ChevronDownIcon } from "lucide-react";
import {
  Button,
  composeRenderProps,
  Disclosure,
  DisclosureGroup,
  DisclosurePanel,
} from "react-aria-components";
import { AccordionStyles } from "./styles";

export interface AccordionRootProps
  extends React.ComponentProps<typeof DisclosureGroup> {}

export function AccordionRoot(props: AccordionRootProps) {
  return <DisclosureGroup data-slot="accordion-root" {...props} />;
}

export interface AccordionItemProps
  extends React.ComponentProps<typeof Disclosure> {}

export function AccordionItem({ className, ...props }: AccordionItemProps) {
  return (
    <Disclosure
      className={composeRenderProps(className, (className) =>
        AccordionStyles.Item({ className })
      )}
      data-slot="accordion-item"
      {...props}
    />
  );
}

export interface AccordionTriggerProps
  extends Omit<React.ComponentProps<typeof Button>, "slot"> {}

export function AccordionTrigger({
  className,
  children,
  ...props
}: AccordionTriggerProps) {
  return (
    <Button
      className={composeRenderProps(className, (className) =>
        AccordionStyles.Trigger({ className })
      )}
      data-slot="accordion-trigger"
      slot="trigger"
      {...props}
    >
      {composeRenderProps(children, (children) => (
        <>
          {children}
          <ChevronDownIcon className={AccordionStyles.Icon()} />
        </>
      ))}
    </Button>
  );
}

export interface AccordionContentProps
  extends React.ComponentProps<typeof DisclosurePanel> {}

export function AccordionContent({
  className,
  children,
  ...props
}: AccordionContentProps) {
  return (
    <DisclosurePanel
      className={composeRenderProps(className, (className) =>
        AccordionStyles.Content({ className })
      )}
      data-slot="accordion-content"
      {...props}
    >
      <div>{children}</div>
    </DisclosurePanel>
  );
}
src/registry/ui/accordion/styles.ts
import { compose, cva } from "~/registry/lib/cva";
import { CollapsibleStyles } from "~/registry/ui/collapsible";

export const AccordionStyles = {
  Content: compose(
    CollapsibleStyles.Content,
    cva({
      base: ["*:pb-4"],
    })
  ),
  Icon: cva({
    base: [
      "pointer-events-none size-4 shrink-0 translate-y-0.5 text-muted-foreground transition-transform duration-200 group-expanded:rotate-180",
    ],
  }),
  Item: cva({
    base: ["group border-b last:border-b-0"],
  }),
  Trigger: cva({
    base: [
      "flex w-full flex-1 items-start justify-between gap-4 rounded-md py-4 text-left font-medium text-sm outline-none transition-all",
      "hover:underline",
      "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
      "disabled:pointer-events-none disabled:opacity-50",
    ],
  }),
};
src/registry/ui/accordion/namespace.ts
export {
  AccordionContent as Content,
  AccordionItem as Item,
  AccordionRoot as Root,
  AccordionTrigger as Trigger,
} from "./accordion";

More from kanpeki

Browse all components in this library →