Skip to content

Accordion

An accordion is a vertically stacked group of collapsible sections. An accordion is composed of grouped buttons and panels. When a user selects an accordion button, its corresponding panel should switch between 'open' and 'collapsed' states.

Accordions follow many consistent patterns but do allow for some variability in behavior. For example, some accordions only allow one panel to be open at a time, where others may allow multiple or all panels to be open simultaneously. Similarly, many accordions will allow all panels to be simultaneously collapsed, while others may require one panel to be open at all times.

If you are familiar with the disclosure pattern, an accordion will feel very similar. The key distinction is that a disclosure is a standalone component that consists of a single button-panel-group. Because of this, you cannot navigate between different disclosures with a keyboard the same way you can with an accordion. To provide users with a predictable behavior between components, it is important to keep disclosures and accordions visually distinct across your app.

Installation

From the command line in your project directory, run npm install @reach/accordion or yarn add @reach/accordion. Then import the components and styles that you need:

npm install @reach/accordion# oryarn add @reach/accordion
import {  Accordion,  AccordionItem,  AccordionButton,  AccordionPanel,} from "@reach/accordion";import "@reach/accordion/styles.css";

Usage

function Example() {  return (    <Accordion>      <AccordionItem>        <h3>          <AccordionButton>Step 1: Do a thing</AccordionButton>        </h3>        <AccordionPanel>          Here are some detailed instructions about doing a thing. I am very          complex and probably contain a lot of content, so a user can hide or          show me by clicking the button above.        </AccordionPanel>      </AccordionItem>      <AccordionItem>        <h3>          <AccordionButton>Step 2: Do another thing</AccordionButton>        </h3>        <AccordionPanel>          Here are some detailed instructions about doing yet another thing.          There are a lot of things someone might want to do, so I am only going          to talk about doing that other thing. I'll let my fellow accordion          items go into detail about even more things.        </AccordionPanel>      </AccordionItem>    </Accordion>  );}

Accordion Headings

With most accordion components, the AccordionPanel is treated as a semantic region of the document, similar to an HTML section or main tag. By default, we assign role="region" and to each AccordionPanel, along with aria-labelledby referencing the associated AccordionButton component.

To improve the semantics of the markup further, the ARIA guidelines dictate that each accordion item's button should be wrapped in an element with role="heading", or more simply, and HTML heading tag. Because headings are necessarily dependent on the context of their surrounding content, we do not wrap the AccordionButton inside of a heading tag by default. It is up to each developer to implement this detail in a way that makes sense for their application.

You can abstract a solution in a variety of ways. Perhaps you write an AccordionHeader wrapper component that accepts a headingLevel prop:

function AccordionHeader({ headingLevel = 2, props }) {  let Comp = "h" + headingLevel;  return (    <Comp>      <AccordionButton {...props} />    </Comp>  );}

You can also create a context-aware heading component so that the heading level increments appropriately as content is nested:

(() => {  const HeadingContext = createContext(2);  function MyAccordionSection(props) {    return (      <div>        <Heading>How to do a thing</Heading>        <p>          Below I am going to explain how you might do a thing, in two very          important steps.        </p>        <Accordion>          <ContextAwareAccordionItem>            <AccordionHeader>Step 1: Do a thing</AccordionHeader>            <AccordionPanel>              Here are some detailed instructions about doing a thing. I am very              complex and probably contain a lot of content, so a user can hide              or show me by clicking the button above.            </AccordionPanel>          </ContextAwareAccordionItem>          <AccordionItem>            <AccordionHeader>Step 2: Do another thing</AccordionHeader>            <AccordionPanel>              Here are some detailed instructions about doing yet another thing.              There are a lot of things someone might want to do, so I am only              going to talk about doing that other thing. I'll let my fellow              accordion items go into detail about even more things.            </AccordionPanel>          </AccordionItem>        </Accordion>      </div>    );  }  function AccordionHeader(props) {    return (      <Heading>        <AccordionButton {...props} />      </Heading>    );  }  function ContextAwareAccordionItem(props) {    return (      <Section>        <AccordionItem {...props} />      </Section>    );  }  function Heading(props) {    let Comp = "h" + Math.min(useContext(HeadingContext), 6);    return <Comp {...props} />;  }  function Section(props) {    let headingLevel = useContext(HeadingContext);    return (      <HeadingContext.Provider value={headingLevel + 1}>        {props.children}      </HeadingContext.Provider>    );  }  return <MyAccordionSection />;})();

Component API

Accordion

The wrapper component for all other accordion components. Each accordion component will consist of accordion items whose buttons are keyboard navigable using arrow keys.

<Accordion index={index} onChange={(value) => setIndex(value)}>  <AccordionItem>    <h3>      <AccordionButton>Step 1: Do a thing</AccordionButton>    </h3>    <AccordionPanel>...</AccordionPanel>  </AccordionItem>  <AccordionItem>    <h3>      <AccordionButton>Step 2: Do another thing</AccordionButton>    </h3>    <AccordionPanel>...</AccordionPanel>  </AccordionItem></Accordion>

Accordion CSS Selectors

Please see the styling guide.

[data-reach-accordion] {}

collapsible and multiple props

You can use the collapsible prop to dictate that an accordion should allow all panels to be collapsed simultaneously. By default, one panel must be in an open state at all times.

function Example() {  return (    <Accordion collapsible>      <AccordionItem>        <h3>          <AccordionButton>Step 1: Do a thing</AccordionButton>        </h3>        <AccordionPanel>          Integer ad iaculis semper aenean nibh quisque hac eget volutpat, at          dui sem accumsan cras congue mi varius egestas interdum, molestie          blandit sociosqu sodales diam metus erat venenatis.        </AccordionPanel>      </AccordionItem>      <AccordionItem>        <h3>          <AccordionButton>Step 2: Do another thing</AccordionButton>        </h3>        <AccordionPanel>          Hendrerit faucibus litora justo aliquet inceptos gravida felis vel          aenean, natoque fermentum nostra tempus ornare nam diam est, neque          risus aliquam sapien vestibulum sociis integer eros.        </AccordionPanel>      </AccordionItem>    </Accordion>  );}

The multiple prop dictates that any number of panels may be open at the same time. By default, when a user opens a new accordion item, the previously open item will be set to collapsed.

function Example() {  return (    <Accordion multiple>      <AccordionItem>        <h3>          <AccordionButton>Step 1: Do a thing</AccordionButton>        </h3>        <AccordionPanel>          Integer ad iaculis semper aenean nibh quisque hac eget volutpat, at          dui sem accumsan cras congue mi varius egestas interdum, molestie          blandit sociosqu sodales diam metus erat venenatis.        </AccordionPanel>      </AccordionItem>      <AccordionItem>        <h3>          <AccordionButton>Step 2: Do another thing</AccordionButton>        </h3>        <AccordionPanel>          Hendrerit faucibus litora justo aliquet inceptos gravida felis vel          aenean, natoque fermentum nostra tempus ornare nam diam est, neque          risus aliquam sapien vestibulum sociis integer eros.        </AccordionPanel>      </AccordionItem>    </Accordion>  );}

Using both props together dictates that any number of panels in an accordion can be open or collapsed at any time without regard to the state of other accordion items. These props are only relevant for uncontrolled accordion components, as the state of controlled accordion items is determined only by the index prop.

function Example() {  return (    <Accordion collapsible multiple>      <AccordionItem>        <h3>          <AccordionButton>Step 1: Do a thing</AccordionButton>        </h3>        <AccordionPanel>          Integer ad iaculis semper aenean nibh quisque hac eget volutpat, at          dui sem accumsan cras congue mi varius egestas interdum, molestie          blandit sociosqu sodales diam metus erat venenatis.        </AccordionPanel>      </AccordionItem>      <AccordionItem>        <h3>          <AccordionButton>Step 2: Do another thing</AccordionButton>        </h3>        <AccordionPanel>          Hendrerit faucibus litora justo aliquet inceptos gravida felis vel          aenean, natoque fermentum nostra tempus ornare nam diam est, neque          risus aliquam sapien vestibulum sociis integer eros.        </AccordionPanel>      </AccordionItem>    </Accordion>  );}

Controlled Accordion

If you want to control the accordion's open panels, you can do so by passing index and onChange props. The index corresponds with the order of each accordion item as they appear within an accordion. The index value passed sets its corresponding panel to an open state.

const [index, setIndex] = useState(0);return (  <Accordion index={index} onChange={(value) => setIndex(value)}>    <AccordionItem {...items[0].props} />    <AccordionItem {...items[1].props} />    <AccordionItem {...items[2].props} />  </Accordion>);

In a controlled accordion, multiple items can be set to open by passing an array of indices to the index prop.

const [indices, setIndices] = useState([0, 2]);function toggleItem(toggledIndex) {  if (indices.includes(toggledIndex)) {    setIndices(indices.filter((currentIndex) => currentIndex !== toggledIndex));  } else {    setIndices([...indices, toggledIndex].sort());  }}return (  <Accordion index={indices} onChange={toggleItem}>    <AccordionItem {...items[0].props} />    <AccordionItem {...items[1].props} />    <AccordionItem {...items[2].props} />  </Accordion>);

Accordion Props

PropTypeRequired
childrennodetrue
collapsiblebooleanfalse
defaultIndexnumber | number[]false
indexnumber | number[]false
multiplebooleanfalse
onChangefuncfalse
readOnlybooleanfalse
Accordion children

children: React.ReactNode

Accordion can accept AccordionItem components as children.

<Accordion>  <AccordionItem>    <AccordionButton />    <AccordionPanel />  </AccordionItem>  <AccordionItem>    <AccordionButton />    <AccordionPanel />  </AccordionItem></Accordion>
Accordion collapsible

collapsible?: boolean

Whether or not all panels of an uncontrolled accordion can be toggled to a closed state. See the collapsible and multiple props section for details. Defaults to false.

Accordion defaultIndex

defaultIndex?: number | number[]

A default value for the open panel's index or indices in an uncontrolled accordion component when it is initially rendered.

<Accordion defaultIndex={1}>  <AccordionItem>    <AccordionButton />    <AccordionPanel>I will be closed on the initial render!</AccordionPanel>  </AccordionItem>  <AccordionItem>    <AccordionButton />    <AccordionPanel>I will be open on the initial render!</AccordionPanel>  </AccordionItem></Accordion>

If an accordion has no defaultIndex, the initially rendered open panel depends on the collapsible prop.

  • If collapsible is set to true, without a defaultIndex no panels will initially be open. Otherwise, the first panel at index 0 will initially be open.

You can only pass an array of indices to defaultIndex if you also set multiple to true.

<Accordion defaultIndex={[0, 1]} multiple>  <AccordionItem>    <AccordionButton />    <AccordionPanel>I will be open on the initial render!</AccordionPanel>  </AccordionItem>  <AccordionItem>    <AccordionButton />    <AccordionPanel>I will also be open on the initial render!</AccordionPanel>  </AccordionItem></Accordion>
Accordion index

index?: number | number[]

The index or array of indices for open accordion panels. The index prop should be used along with onChange to create controlled accordion components.

const [indices, setIndices] = useState([0, 2]);function toggleItem(toggledIndex) {  if (indices.includes(toggledIndex)) {    setIndices(indices.filter((currentIndex) => currentIndex !== toggledIndex));  } else {    setIndices([...indices, toggledIndex].sort());  }}return (  <Accordion index={indices} onChange={toggleItem}>    <AccordionItem {...items[0].props} />    <AccordionItem {...items[1].props} />    <AccordionItem {...items[2].props} />  </Accordion>);
Accordion multiple

multiple?: boolean

Whether or not multiple panels in an uncontrolled accordion can be opened at the same time. See the collapsible and multiple props section for details. Defaults to false.

Accordion onChange

onChange?: (value: number) => void

The callback that is fired when an accordion item's open state is changed.

Accordion readOnly

readOnly?: boolean

Whether or not an uncontrolled accordion is read-only (meaning that the user cannot toggle its state with a normal interaction). Defaults to false.

Generally speaking, you probably want to avoid this, as it can be confusing especially when navigating by keyboard. However, this may be useful if you want to lock an accordion under certain conditions (perhaps user authentication is required to access the content). In these instances, you may want to include an alert when a user tries to activate a read-only accordion panel to let them know why it does not toggle as may be expected.

AccordionItem

A group that wraps an accordion's button and panel components.

AccordionItem CSS Selectors

Please see the styling guide.

[data-reach-accordion-item] {  /* styles for all accordion items */}[data-reach-accordion-item][data-state="open"] {  /* styles for all open accordion items */}[data-reach-accordion-item][data-state="collapsed"] {  /* styles for all collapsed accordion items */}[data-reach-accordion-item][data-disabled] {  /* styles for all disabled accordion items */}[data-reach-accordion-item][data-read-only] {  /* styles for all read-only accordion items */}

AccordionItem Props

PropTypeRequired
asstring | Componentfalse
childrennodetrue
disabledbooleanfalse
AccordionItem as

as?: keyof JSX.IntrinsicElements | React.ComponentType

A string representing an HTML element or a React component that will tell the AccordionItem what element to render. Defaults to div.

NOTE: Many semantic elements, such as button elements, have meaning to assistive devices and browsers that provide context for the user and, in many cases, provide or restrict interactive behaviors. Use caution when overriding our defaults and make sure that the element you choose to render provides the same experience for all users.

AccordionItem children

children: React.ReactNode

An AccordionItem expects to receive an AccordionButton and AccordionPanel components as its children, though you can also nest other components within an AccordionItem if you want some persistant content that is relevant to the section but not collapsible when the AccordionButton is toggled.

<Accordion defaultIndex={[0, 1]} multiple>  <AccordionItem>    <AccordionButton>Step 1: Do this important thing</AccordionButton>    <AccordionPanel>Detailed instructions about step 1.</AccordionPanel>  </AccordionItem>  <AccordionItem>    <AccordionButton>Step 2: Do this other important thing</AccordionButton>    <AccordionPanel>Detailed instructions about step 1.</AccordionPanel>    {/* the following component will not collapse! */}    <SomeCalloutBox>      Important: you should always consult the user manual before doing step 2!    </SomeCalloutBox>  </AccordionItem></Accordion>
AccordionItem disabled

disabled?: boolean

Whether or not an accordion panel is disabled from user interaction. Defaults to false.

AccordionButton

The trigger button a user clicks to interact with an accordion.

AccordionButton CSS Selectors

Please see the styling guide.

[data-reach-accordion-button] {  /* styles for buttons in all accordion items */}[data-reach-accordion-button][aria-expanded] {  /* styles for buttons in open accordion items */}[data-reach-accordion-button][disabled] {  /* styles for all buttons in disabled accordion items */}

AccordionButton Props

PropTypeRequired
asstring | Componentfalse
childrennodetrue
AccordionButton as

as?: keyof JSX.IntrinsicElements | React.ComponentType

A string representing an HTML element or a React component that will tell the AccordionButton what element to render. Defaults to button.

NOTE: Many semantic elements, such as button elements, have meaning to assistive devices and browsers that provide context for the user and, in many cases, provide or restrict interactive behaviors. Use caution when overriding our defaults and make sure that the element you choose to render provides the same experience for all users.

AccordionButton children

children: React.ReactNode

Typically a text string that serves as a label for the accordion, though nested DOM nodes can be passed as well so long as they are valid children of interactive elements.

If you need to group interactive elements within an accordion's button, we recommend grouping the button inside of a wrapper element rather than including invalid HTML inside the button:

(() => {  function Example() {    return (      <Accordion>        <AccordionItem>          <GroupedAccordionHeader>Option 1</GroupedAccordionHeader>          <StyledAccordionPanel>            Ante rhoncus facilisis iaculis nostra faucibus vehicula ac            consectetur pretium, lacus nunc consequat id viverra facilisi ligula            eleifend, congue gravida malesuada proin scelerisque luctus est            convallis.          </StyledAccordionPanel>        </AccordionItem>        <AccordionItem>          <GroupedAccordionHeader>Option 2</GroupedAccordionHeader>          <StyledAccordionPanel>            Ante rhoncus facilisis iaculis nostra faucibus vehicula ac            consectetur pretium, lacus nunc consequat id viverra facilisi ligula            eleifend, congue gravida malesuada proin scelerisque luctus est            convallis.          </StyledAccordionPanel>        </AccordionItem>        <AccordionItem>          <GroupedAccordionHeader>Option 3</GroupedAccordionHeader>          <StyledAccordionPanel>            Ante rhoncus facilisis iaculis nostra faucibus vehicula ac            consectetur pretium, lacus nunc consequat id viverra facilisi ligula            eleifend, congue gravida malesuada proin scelerisque luctus est            convallis.          </StyledAccordionPanel>        </AccordionItem>      </Accordion>    );  }  function StyledAccordionPanel(props) {    return <AccordionPanel style={{ padding: 10 }} {...props} />;  }  function GroupedAccordionHeader({ children }) {    return (      <div        style={{          alignItems: "center",          background: "#eee",          border: "1px solid #888",          borderRadius: 3,          margin: "9px 0",          display: "flex",          justifyContent: "space-between",          padding: "4px 10px",        }}      >        <AccordionButton          style={{            appearance: "none",            background: 0,            border: 0,            boxShadow: "none",            color: "inherit",            display: "block",            textAlign: "inherit",            flexGrow: 1,            flexShrink: 0,            font: "inherit",            fontWeight: "bolder",            margin: 0,            padding: "10px 0",          }}        >          {children}        </AccordionButton>        <Menu>          <MenuButton style={{ margin: 0 }}>            <span>Actions</span>          </MenuButton>          <MenuList>            <MenuItem onSelect={() => console.log("Download")}>              Download            </MenuItem>            <MenuItem onSelect={() => console.log("Copy")}>              Create a Copy            </MenuItem>          </MenuList>        </Menu>      </div>    );  }  return <Example />;})();

AccordionPanel

The collapsible panel in which inner content for an accordion item is rendered.

AccordionPanel CSS Selectors

Please see the styling guide.

[data-reach-accordion-panel] {  /* styles for all accordion panels */}[data-reach-accordion-panel][data-state="open"] {  /* styles for all open accordion panels */}[data-reach-accordion-panel][data-state="collapsed"] {  /* styles for all collapsed accordion panels */}[data-reach-accordion-panel][data-disabled] {  /* styles for all disabled accordion panels */}

AccordionPanel Props

PropTypeRequired
asstring | Componentfalse
childrennodetrue
AccordionPanel as

as?: keyof JSX.IntrinsicElements | React.ComponentType

A string representing an HTML element or a React component that will tell the AccordionPanel what element to render. Defaults to div.

NOTE: Many semantic elements, such as button elements, have meaning to assistive devices and browsers that provide context for the user and, in many cases, provide or restrict interactive behaviors. Use caution when overriding our defaults and make sure that the element you choose to render provides the same experience for all users.

AccordionPanel children

children: React.ReactNode

Inner collapsible content for the accordion item.

useAccordionContext

function useAccordionContext(): { id: string | undefined; openPanels: number[] }

A hook that exposes data for a given Accordion component to its descendants.

useAccordionItemContext

function useAccordionItemContext(): { index: number; isExpanded: boolean }

A hook that exposes data for a given AccordionItem component to its descendants.