Disclosure

Rows that expand and collapse in place, alone or as an accordion.

Disclosure shows and hides a block of content under a heading. A DisclosureGroup stacks several Disclosures into an accordion. Each row has a DisclosureTrigger, the heading and the button that toggles it, and a DisclosurePanel, the content it reveals. The plus icon turns to a cross when the row is expanded.

<Disclosure>
<DisclosureTrigger>Advanced settings</DisclosureTrigger>
<DisclosurePanel>
  Set a custom RPC endpoint and gas limit for this project.
</DisclosurePanel>
</Disclosure>

Variants

Set variant on the DisclosureGroup and every row in it follows. A Disclosure used on its own takes variant directly.

Default

<DisclosureGroup>
<Disclosure id="network">
  <DisclosureTrigger>Network</DisclosureTrigger>
  <DisclosurePanel>Requests go to Base Mainnet unless you pick Base Sepolia.</DisclosurePanel>
</Disclosure>
<Disclosure id="limits">
  <DisclosureTrigger>Rate limits</DisclosureTrigger>
  <DisclosurePanel>Each key allows 50 requests per second.</DisclosurePanel>
</Disclosure>
<Disclosure id="billing">
  <DisclosureTrigger>Billing</DisclosureTrigger>
  <DisclosurePanel>Usage is billed on the first of each month.</DisclosurePanel>
</Disclosure>
</DisclosureGroup>

Plain rows at the product type size, for settings, details and other secondary content inside a page.

Divided

variant="divided" puts a rule above every row and below the last (a subtler one in dark mode), and uses the larger brand type steps. Its main use is a list of frequently asked questions, like the FAQ sections on base.org, but it suits any list of expandable rows that should read as one block.

Base is a secure, low-cost, builder-friendly Ethereum L2 built to bring the next billion users onchain.

<DisclosureGroup variant="divided" defaultExpandedKeys={['what']}>
<Disclosure id="what">
  <DisclosureTrigger>What is Base?</DisclosureTrigger>
  <DisclosurePanel>
    Base is a secure, low-cost, builder-friendly Ethereum L2 built to bring
    the next billion users onchain.
  </DisclosurePanel>
</Disclosure>
<Disclosure id="cost">
  <DisclosureTrigger>How much does a transaction cost?</DisclosureTrigger>
  <DisclosurePanel>
    Most transactions cost less than a cent.
  </DisclosurePanel>
</Disclosure>
<Disclosure id="start">
  <DisclosureTrigger>How do I start building?</DisclosureTrigger>
  <DisclosurePanel>
    Read the docs and deploy your first contract in minutes.
  </DisclosurePanel>
</Disclosure>
</DisclosureGroup>

This replaces base-org-ui's FaqAccordion: map each question to a DisclosureTrigger and each answer to a DisclosurePanel. base.org opens the first row on load; do the same with defaultExpandedKeys.

Expanding rows

One at a time

A DisclosureGroup keeps one row expanded at a time: opening a row closes the one that was open. Give each Disclosure an id, then set the rows that start open with defaultExpandedKeys, or control them with expandedKeys and onExpandedChange.

Several at once

Set allowsMultipleExpanded on the group to let rows open independently.

<DisclosureGroup allowsMultipleExpanded>
<Disclosure id="logs">
  <DisclosureTrigger>Logs</DisclosureTrigger>
  <DisclosurePanel>Logs are kept for 30 days.</DisclosurePanel>
</Disclosure>
<Disclosure id="alerts">
  <DisclosureTrigger>Alerts</DisclosureTrigger>
  <DisclosurePanel>Alerts go to every project owner.</DisclosurePanel>
</Disclosure>
</DisclosureGroup>

Disabled

Set isDisabled on a Disclosure to lock one row, or on the group to lock them all. A disabled row keeps its current state and its trigger dims.

<Disclosure isDisabled>
<DisclosureTrigger>Team settings</DisclosureTrigger>
<DisclosurePanel>Only owners can change team settings.</DisclosurePanel>
</Disclosure>

When to use

Use Disclosure to let the reader expand content they may not need, under a heading that tells them what it holds, and DisclosureGroup to stack several of those rows as an accordion.

Use something else when:

  • The sections are peer views of one object that the reader switches between. Use Tabs.
  • The content belongs to a control and floats above the page. Use Popover.
  • The reader must respond before they continue. Use Dialog.
  • A container only needs to animate a change in its own size, with no toggle. Use Animate Height.

Usage

Do

Write a trigger that says what the panel holds, so the reader can decide whether to open it without opening it.

Don't

Hide content every reader needs, such as a required step or an error, inside a collapsed row.

The trigger renders inside a heading, level 3 by default. Set level on DisclosureTrigger to fit the page outline.

Props

DisclosureGroup

PropTypeDefault
allowsMultipleExpanded

Whether multiple items can be expanded at the same time.

TypebooleanDefault—
children

The children of the component. A function may be provided to alter the children based on

TypeChildrenOrFunction<DisclosureGroupRenderProps>Default—
className

The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the

TypeClassNameOrFunction<DisclosureGroupRenderProps>Default—
defaultExpandedKeys

The initial expanded keys in the group (uncontrolled).

TypeIterable<Key>Default—
expandedKeys

The currently expanded keys in the group (controlled).

TypeIterable<Key>Default—
isDisabled

Whether all items are disabled.

TypebooleanDefault—
variant

Appearance of every Disclosure in the group. `default` stacks plain rows.

Type"default" | "divided"Defaultdefault

Disclosure

PropTypeDefault
children

The children of the component. A function may be provided to alter the children based on

TypeChildrenOrFunction<DisclosureGroupRenderProps>Default—
className

The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the

TypeClassNameOrFunction<DisclosureGroupRenderProps>Default—
defaultExpanded

Whether the disclosure is expanded by default (uncontrolled).

TypebooleanDefault—
isDisabled

Whether the disclosure is disabled.

TypebooleanDefault—
isExpanded

Whether the disclosure is expanded (controlled).

TypebooleanDefault—
variant

Appearance of the row. Inside a DisclosureGroup the group's variant wins.

Type"default" | "divided"Default—

DisclosureTrigger

PropTypeDefault
children

The children of the component. A function may be provided to alter the children based on

TypeChildrenOrFunction<DisclosureGroupRenderProps>Default—
className

The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the

TypeClassNameOrFunction<ButtonRenderProps>Default—
isDisabled

Whether the button is disabled.

TypebooleanDefault—
isPending

Whether the button is in a pending state. This disables press and hover events

TypebooleanDefault—
level

Heading level of the element wrapping the button. Defaults to 3.

TypenumberDefault3
type

The behavior of the button when used in an HTML form.

Type"button" | "submit" | "reset"Default'button'

DisclosurePanel

PropTypeDefault
childrenrequired

The children of the component.

TypeReactNodeDefault—
className

The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the

TypeClassNameOrFunction<DisclosureGroupRenderProps>Default—
label

The content to display as the label.

TypeReactNodeDefault—
labelElementType

The HTML element used to render the label, e.g. 'label', or 'span'.

TypeElementTypeDefault'label'
role

The accessibility role for the disclosure's panel.

Type"group" | "region"Default'group'

Accessibility

Disclosure wraps React Aria's Disclosure, DisclosurePanel and DisclosureGroup. The trigger is a button inside a heading; Enter and Space toggle it, and it carries aria-expanded and aria-controls. The panel is a group labelled by its trigger. A collapsed panel is hidden with hidden="until-found", so find in page still reaches its text and opens the row.

The plus icon is decorative and hidden from assistive technology; the expanded state is announced from aria-expanded.

The panel opens and closes with a 200ms height and fade, and the icon rotates over 200ms; both ease out. Both are instant when the reader prefers reduced motion or toggles the row from the keyboard. The panel reaches 8px past its content on the sides and top, so focus rings on links and buttons inside it are not clipped.