Skip to content
Nizivo UI

Accordion

Sections that open and close under their headings, such as FAQs or a checkout's order summary. Each item is a native details element, so it works with no JavaScript, and items that share a name open one at a time.

Server component, no client JS

Import

import { Accordion } from '@nizivo/react/accordion';

// Once per app (Getting started), after
// @nizivo/tokens/tokens.css and @nizivo/react/css/base.css
import '@nizivo/react/css/accordion.css';

Static or interactive

Server. No client JavaScript: each item is a native <details> with its <summary> as the heading, so it opens and closes before and without hydration, from the pointer and the keyboard. The browser's find in page opens a closed item that holds the match. How to choose.

Parts

Accordion.Root holds the items. Each Accordion.Item is a <details>, with an Accordion.Trigger (its <summary>: the heading, with a chevron at its end) and an Accordion.Panel (the content). defaultOpen opens an item on the first render; after that the browser keeps the open state.

One at a time

Give Accordion.Root a name and every item that sets none gets it: opening one closes the others, with no script, as <details name> does. The name groups items across the whole page, so use one no other accordion on the page uses. Without a name, items open independently. Firefox 129, the oldest Firefox Nizivo supports, does not group them: there, several items can be open at once.

The heading

The trigger's text is a heading for its panel, not a page heading: put an <h3> (or the level the page needs) inside it when the items should appear in a screen reader's headings list. indicator replaces the chevron, which is decorative.

Examples

Basic

Frequently asked questions: one answer open at a time (name), with no client JavaScript.

Source only

It opens on page load (defaultOpen), which would move focus on this page, so it is shown as source only.
import type { JSX } from 'react';
import { Accordion } from '@nizivo/react/accordion';

export function AccordionExample(): JSX.Element {
  return (
    <Accordion.Root name="shipping-faq" style={{ maxInlineSize: 560 }}>
      <Accordion.Item defaultOpen>
        <Accordion.Trigger>When will my order ship?</Accordion.Trigger>
        <Accordion.Panel>
          Orders placed before 2 pm ship the same working day. You get a tracking link by email.
        </Accordion.Panel>
      </Accordion.Item>
      <Accordion.Item>
        <Accordion.Trigger>Can I change my delivery address?</Accordion.Trigger>
        <Accordion.Panel>
          Yes, until the order ships. Open the order from your account and choose Edit address.
        </Accordion.Panel>
      </Accordion.Item>
      <Accordion.Item>
        <Accordion.Trigger>How do returns work?</Accordion.Trigger>
        <Accordion.Panel>
          Return any unused item within 30 days. The return label is free for orders over €50.
        </Accordion.Panel>
      </Accordion.Item>
    </Accordion.Root>
  );
}

Order Summary

A checkout's collapsible order summary: independent items, without name.

Order summary (3 items) · €84.00
Sand mug ×2 · €48.00. Stoneware bowl ×1 · €36.00.
Add a promo code
Codes apply at the payment step.
import type { JSX } from 'react';
import { Accordion } from '@nizivo/react/accordion';

export function AccordionOrderSummaryExample(): JSX.Element {
  return (
    <Accordion.Root style={{ maxInlineSize: 400 }}>
      <Accordion.Item>
        <Accordion.Trigger>Order summary (3 items) · €84.00</Accordion.Trigger>
        <Accordion.Panel>Sand mug ×2 · €48.00. Stoneware bowl ×1 · €36.00.</Accordion.Panel>
      </Accordion.Item>
      <Accordion.Item>
        <Accordion.Trigger>Add a promo code</Accordion.Trigger>
        <Accordion.Panel>Codes apply at the payment step.</Accordion.Panel>
      </Accordion.Item>
    </Accordion.Root>
  );
}

Parts

Use the parts through the namespace (Accordion.Root) or by their flat names (AccordionRoot); both work from Server Components.

Accordion.Root

Server component Renders data-slot="accordion" with the class nz-accordion. Props: AccordionRootProps. Source: src/accordion/accordion.tsx.

NameTypeDefaultDescription
childrenReactNode–

Accordion.Item elements.

namestring–

Makes the items exclusive: each Accordion.Item that sets no name gets this one, so opening one closes the others. It groups <details> across the whole document, so use a name no other accordion on the page uses. Without it, items open independently.

renderRenderProp<NoState>–

Must render a <div>.

  • Also takes every attribute of <div>; children is redeclared above. (ComponentPropsWithRef<"div">)

render takes an element, or, in client code, a function of the props and no state.

Accordion.Item

Server component Renders data-slot="accordion-item" with the class nz-accordion-item. Props: AccordionItemProps. Source: src/accordion/accordion.tsx.

NameTypeDefaultDescription
childrenReactNode–

An Accordion.Trigger, then an Accordion.Panel.

defaultOpenbooleanfalse

Open on the first render. The browser keeps the open state after that.

namestring–

The exclusive group; set by Accordion.Root name when left out.

renderRenderProp<NoState>–

Must render a <details>.

  • Also takes every attribute of <details> except open; children, name are redeclared above. (Omit<ComponentPropsWithRef<"details">, "open">)

render takes an element, or, in client code, a function of the props and no state.

Accordion.Trigger

Server component Renders data-slot="accordion-trigger" with the class nz-accordion-trigger. Props: AccordionTriggerProps. Source: src/accordion/accordion.tsx.

NameTypeDefaultDescription
childrenrequiredReactNode–
indicatorReactNode–

Replaces the chevron at the end. Decorative (aria-hidden).

renderRenderProp<NoState>–

Must render a <summary>.

  • Also takes every attribute of <summary>; children is redeclared above. (ComponentPropsWithRef<"summary">)

render takes an element, or, in client code, a function of the props and no state.

Accordion.Panel

Server component Renders data-slot="accordion-panel" with the class nz-accordion-panel. Props: AccordionPanelProps. Source: src/accordion/accordion.tsx.

NameTypeDefaultDescription
renderRenderProp<NoState>–

Must render a <div>.

  • Also takes every attribute of <div>. (ComponentPropsWithRef<"div">)

render takes an element, or, in client code, a function of the props and no state.

Other exports

AccordionItemProps type

import type { AccordionItemProps } from '@nizivo/react/accordion'

The props of Accordion.Item, listed above.

AccordionPanelProps type

import type { AccordionPanelProps } from '@nizivo/react/accordion'

The props of Accordion.Panel, listed above.

AccordionRootProps type

import type { AccordionRootProps } from '@nizivo/react/accordion'

The props of Accordion.Root, listed above.

AccordionTriggerProps type

import type { AccordionTriggerProps } from '@nizivo/react/accordion'

The props of Accordion.Trigger, listed above.

Data attributes and slots

Style parts by class (.nz-<slot>) and state by data attribute. Attribute meanings come from contracts/vocabulary.json.

SlotRendered byData attributes
accordion
.nz-accordion
Accordion.Root
root (takes render)
none
accordion-indicator
.nz-accordion-indicator
Accordion.Trigger
inner element (CSS only)
none
accordion-item
.nz-accordion-item
Accordion.Item
root (takes render)
none
accordion-panel
.nz-accordion-panel
Accordion.Panel
root (takes render)
none
accordion-title
.nz-accordion-title
Accordion.Trigger
inner element (CSS only)
none
accordion-trigger
.nz-accordion-trigger
Accordion.Trigger
root (takes render); interactive, focus: self
none

CSS hooks

Set a hook on the component's class or any ancestor to restyle it without specificity games; leave it unset for the default shown.

HookDefaultRead as
--nz-accordion-border--nz-border-subtle
  • border-block-end on .nz-accordion-item
--nz-accordion-padding-inline--nz-space-0
  • padding-inline on .nz-accordion-panel
  • padding-inline on .nz-accordion-trigger
--nz-accordion-trigger-fg--nz-text-primary
  • color on .nz-accordion-trigger

Layers: nz.components. Classes: .nz-accordion, .nz-accordion-indicator, .nz-accordion-item, .nz-accordion-panel, .nz-accordion-title, .nz-accordion-trigger.

Tokens this stylesheet reads (15)

--nz-border-subtle, --nz-border-width-default, --nz-duration-fast, --nz-easing-standard, --nz-font-body-m, --nz-font-label-m-strong, --nz-icon-secondary, --nz-radius-sm, --nz-size-icon-sm, --nz-space-0, --nz-space-3, --nz-space-4, --nz-text-primary, --nz-text-secondary, --nz-tracking-label-m

Accessibility

These parts are interactive. Each gets the focus outline and touch-action rule from @nizivo/react/css/base.css (the nz.base layer), so the floor survives a restyle:

  • accordion-trigger: draws the focus outline itself on keyboard focus.

Keyboard

KeysAction
TabMoves focus to the next item's heading.
Enter, SpaceOn a heading, opens or closes its item.

Source: native <details> and <summary> (accordion.tsx). The browser lane (test/browser/accordion.spec.tsx) checks the pointer, Tab, Enter and Space, exclusive items and the chevron.

Figma

Node ids from design/component-map.json, which nz gen writes from component.json. The links open the Nizivo Figma library, which may ask you to sign in.

On this page