alpha

useDisclosure

Provides the behavior and accessibility implementation for a disclosure component.

installyarn add @react-aria/disclosure
version3.0.0-alpha.0
usageimport {useDisclosure} from '@react-aria/disclosure'

API#


useDisclosure( props: AriaDisclosureProps, state: DisclosureState, ref?: RefObject<Elementnull> ): DisclosureAria

Features#


A disclosure is a collapsible section of content. It is composed of a trigger button and a panel that contains the content. useDisclosure can be used to implement these in an accessible way.

  • Support for mouse, touch, and keyboard interactions to open and close the disclosure
  • Support for disabled disclosures
  • Follows the disclosure ARIA pattern, semantically linking the trigger button and panel
  • Uses hidden="until-found" in supported browsers, enabling find-in-page search support and improved search engine visibility for collapsed content

Anatomy#


LandscapeButtonLandscape contentPanel

A disclosure consists of a trigger button and a panel. Clicking on or pressing Enter or Space while the trigger button is focused toggles the visibility of the panel.

useDisclosure returns props to spread onto the trigger button and panel.

NameTypeDescription
buttonPropsAriaButtonPropsProps for the disclosure button.
panelPropsHTMLAttributes<HTMLElement>Props for the disclosure panel.

State is managed by the useDisclosureState hook in @react-stately/disclosure. The state object should be passed as an option to useDisclosure.

Example#


This example displays a basic disclosure with a button that toggles the visibility of the panel.

import {useDisclosureState} from '@react-stately/disclosure';
import {useDisclosure} from '@react-aria/disclosure';
import {useButton} from '@react-aria/button';

function Disclosure(props) {
  let state = useDisclosureState(props);
  let panelRef = React.useRef<HTMLDivElement | null>(null);
  let triggerRef = React.useRef<HTMLButtonElement | null>(null);
  let { buttonProps: triggerProps, panelProps } = useDisclosure(
    props,
    state,
    panelRef
  );
  let { buttonProps } = useButton(triggerProps, triggerRef);

  return (
    <div className="disclosure">
      <h3>
        <button className="trigger" ref={triggerRef} {...buttonProps}>
          <svg viewBox="0 0 24 24">
            <path d="m8.25 4.5 7.5 7.5-7.5 7.5" />
          </svg>
          {props.title}
        </button>
      </h3>
      <div className="panel" ref={panelRef} {...panelProps}>
        <p>
          {props.children}
        </p>
      </div>
    </div>
  );
}

<Disclosure title="System Requirements">
  Details about system requirements here.
</Disclosure>
import {useDisclosureState} from '@react-stately/disclosure';
import {useDisclosure} from '@react-aria/disclosure';
import {useButton} from '@react-aria/button';

function Disclosure(props) {
  let state = useDisclosureState(props);
  let panelRef = React.useRef<HTMLDivElement | null>(null);
  let triggerRef = React.useRef<HTMLButtonElement | null>(
    null
  );
  let { buttonProps: triggerProps, panelProps } =
    useDisclosure(props, state, panelRef);
  let { buttonProps } = useButton(triggerProps, triggerRef);

  return (
    <div className="disclosure">
      <h3>
        <button
          className="trigger"
          ref={triggerRef}
          {...buttonProps}
        >
          <svg viewBox="0 0 24 24">
            <path d="m8.25 4.5 7.5 7.5-7.5 7.5" />
          </svg>
          {props.title}
        </button>
      </h3>
      <div className="panel" ref={panelRef} {...panelProps}>
        <p>
          {props.children}
        </p>
      </div>
    </div>
  );
}

<Disclosure title="System Requirements">
  Details about system requirements here.
</Disclosure>
import {useDisclosureState} from '@react-stately/disclosure';
import {useDisclosure} from '@react-aria/disclosure';
import {useButton} from '@react-aria/button';

function Disclosure(
  props
) {
  let state =
    useDisclosureState(
      props
    );
  let panelRef = React
    .useRef<
      | HTMLDivElement
      | null
    >(null);
  let triggerRef = React
    .useRef<
      | HTMLButtonElement
      | null
    >(null);
  let {
    buttonProps:
      triggerProps,
    panelProps
  } = useDisclosure(
    props,
    state,
    panelRef
  );
  let { buttonProps } =
    useButton(
      triggerProps,
      triggerRef
    );

  return (
    <div className="disclosure">
      <h3>
        <button
          className="trigger"
          ref={triggerRef}
          {...buttonProps}
        >
          <svg viewBox="0 0 24 24">
            <path d="m8.25 4.5 7.5 7.5-7.5 7.5" />
          </svg>
          {props.title}
        </button>
      </h3>
      <div
        className="panel"
        ref={panelRef}
        {...panelProps}
      >
        <p>
          {props
            .children}
        </p>
      </div>
    </div>
  );
}

<Disclosure title="System Requirements">
  Details about system
  requirements here.
</Disclosure>
Show CSS
.disclosure {
  .trigger {
    background: none;
    border: none;
    box-shadow: none;
    font-weight: bold;
    font-size: 16px;
    display: flex;
    align-items: center;
    gap: 8px;

    svg {
      rotate: 0deg;
      transition: rotate 200ms;
      width: 12px;
      height: 12px;
      fill: none;
      stroke: currentColor;
      stroke-width: 3px;
    }

    &[aria-expanded="true"] svg {
      rotate: 90deg;
    }
  }
}

.panel {
  margin-left: 32px;
}
.disclosure {
  .trigger {
    background: none;
    border: none;
    box-shadow: none;
    font-weight: bold;
    font-size: 16px;
    display: flex;
    align-items: center;
    gap: 8px;

    svg {
      rotate: 0deg;
      transition: rotate 200ms;
      width: 12px;
      height: 12px;
      fill: none;
      stroke: currentColor;
      stroke-width: 3px;
    }

    &[aria-expanded="true"] svg {
      rotate: 90deg;
    }
  }
}

.panel {
  margin-left: 32px;
}
.disclosure {
  .trigger {
    background: none;
    border: none;
    box-shadow: none;
    font-weight: bold;
    font-size: 16px;
    display: flex;
    align-items: center;
    gap: 8px;

    svg {
      rotate: 0deg;
      transition: rotate 200ms;
      width: 12px;
      height: 12px;
      fill: none;
      stroke: currentColor;
      stroke-width: 3px;
    }

    &[aria-expanded="true"] svg {
      rotate: 90deg;
    }
  }
}

.panel {
  margin-left: 32px;
}

Usage#


The following examples show how to use the Disclosure component created in the above example.

Default expansion#

Whether or not the disclosure is expanded or not by default can be set with the defaultExpanded prop.

<Disclosure title="System Requirements" defaultExpanded>
  Details about system requirements here.
</Disclosure>
<Disclosure title="System Requirements" defaultExpanded>
  Details about system requirements here.
</Disclosure>
<Disclosure
  title="System Requirements"
  defaultExpanded
>
  Details about system
  requirements here.
</Disclosure>

Controlled expansion#

Expansion can be controlled using the isExpanded prop, paired with the onExpandedChange event. The onExpandedChange event is fired when the user presses the trigger button.

function ControlledDisclosure(props) {
  let [isExpanded, setExpanded] = React.useState(false);

  return (
    <Disclosure
      title="System Requirements"
      isExpanded={isExpanded}
      onExpandedChange={setExpanded}
    >
      Details about system requirements here.
    </Disclosure>
  );
}
function ControlledDisclosure(props) {
  let [isExpanded, setExpanded] = React.useState(false);

  return (
    <Disclosure
      title="System Requirements"
      isExpanded={isExpanded}
      onExpandedChange={setExpanded}
    >
      Details about system requirements here.
    </Disclosure>
  );
}
function ControlledDisclosure(
  props
) {
  let [
    isExpanded,
    setExpanded
  ] = React.useState(
    false
  );

  return (
    <Disclosure
      title="System Requirements"
      isExpanded={isExpanded}
      onExpandedChange={setExpanded}
    >
      Details about
      system requirements
      here.
    </Disclosure>
  );
}

Disabled#

A disclosure can be disabled with the isDisabled prop. This will disable the trigger button and prevent the panel from being opened or closed.

<Disclosure title="System Requirements" isDisabled>
  Details about system requirements here.
</Disclosure>
<Disclosure title="System Requirements" isDisabled>
  Details about system requirements here.
</Disclosure>
<Disclosure
  title="System Requirements"
  isDisabled
>
  Details about system
  requirements here.
</Disclosure>

Disclosure Group (Accordion)#


See the docs for useDisclosureGroupState in react-stately for an example of a disclosure group.