useBreadcrumbs

Provides the behavior and accessibility implementation for a breadcrumbs component. Breadcrumbs display a hierarchy of links to the current page or resource in an application.

installyarn add react-aria
version3.23.1
usageimport {useBreadcrumbs, useBreadcrumbItem} from 'react-aria'

API#


useBreadcrumbs( (props: AriaBreadcrumbsProps )): BreadcrumbsAria useBreadcrumbItem( (props: AriaBreadcrumbItemProps, , ref: RefObject<FocusableElement> )): BreadcrumbItemAria

Features#


Breadcrumbs provide a list of links to parent pages of the current page in hierarchical order. useBreadcrumbs and useBreadcrumbItem help implement these in an accessible way.

  • Support for mouse, touch, and keyboard interactions on breadcrumbs
  • Support for navigation links via <a> elements or custom element types via ARIA
  • Localized ARIA labeling support for landmark navigation region
  • Support for disabled breadcrumbs
  • Support for breadcrumbs as a heading

Anatomy#


TrendSub ItemJanuary 2019 AssetsBreadcrumb itemSeparatorCurrent pageNavigation

Breadcrumbs consist of a navigation landmark element and a list of links, typically with a visual separator icon between each item. The last link represents the current page in the hierarchy, with the previous links representing the parent pages of the current page. Each of these parent links can be clicked, tapped, or triggered via the Enter key to navigate to that page. Optionally, breadcrumbs can be used in place of a page title, in which case the last breadcrumb acts as a heading.

useBreadcrumbs returns props to be spread onto the navigation element:

NameTypeDescription
navPropsDOMAttributesProps for the breadcrumbs navigation element.

useBreadcrumbItem returns props to spread onto the individual breadcrumb links:

NameTypeDescription
itemPropsDOMAttributesProps for the breadcrumb item link element.

Example#


This example displays a basic list of breadcrumbs using an HTML <nav> element, and a <ol> for the list of links. Each link is a span because we are handling the interactions locally via onPress. useBreadcrumbItem automatically handles exposing these spans as links to assistive technology.

The chevrons between each link are rendered using a span with aria-hidden="true" so that screen readers do not pick them up. You could also render them similarly using SVG icons, CSS :after, or other techniques.

The last link is non-interactive since it represents the current page. This is passed to the last breadcrumb item by cloning the element and adding the isCurrent prop.

import {useBreadcrumbItem, useBreadcrumbs} from 'react-aria';

function Breadcrumbs(props) {
  let { navProps } = useBreadcrumbs(props);
  let childCount = React.Children.count(props.children);

  return (
    <nav {...navProps}>
      <ol style={{ display: 'flex', listStyle: 'none', margin: 0, padding: 0 }}>
        {React.Children.map(props.children, (child, i) =>
          React.cloneElement(child, { isCurrent: i === childCount - 1 }))}
      </ol>
    </nav>
  );
}

function BreadcrumbItem(props) {
  let ref = React.useRef(null);
  let { itemProps } = useBreadcrumbItem({ ...props, elementType: 'span' }, ref);
  return (
    <li>
      <span
        {...itemProps}
        ref={ref}
        style={{
          color: props.isDisabled ? 'var(--gray)' : 'var(--blue)',
          textDecoration: props.isCurrent || props.isDisabled
            ? 'none'
            : 'underline',
          fontWeight: props.isCurrent ? 'bold' : null,
          cursor: props.isCurrent || props.isDisabled ? 'default' : 'pointer'
        }}
      >
        {props.children}
      </span>
      {!props.isCurrent &&
        <span aria-hidden="true" style={{ padding: '0 5px' }}>{'›'}</span>}
    </li>
  );
}

<Breadcrumbs>
  <BreadcrumbItem onPress={() => alert('Pressed Folder 1')}>
    Folder 1
  </BreadcrumbItem>
  <BreadcrumbItem onPress={() => alert('Pressed Folder 2')}>
    Folder 2
  </BreadcrumbItem>
  <BreadcrumbItem>Folder 3</BreadcrumbItem>
</Breadcrumbs>
import {
  useBreadcrumbItem,
  useBreadcrumbs
} from 'react-aria';

function Breadcrumbs(props) {
  let { navProps } = useBreadcrumbs(props);
  let childCount = React.Children.count(props.children);

  return (
    <nav {...navProps}>
      <ol
        style={{
          display: 'flex',
          listStyle: 'none',
          margin: 0,
          padding: 0
        }}
      >
        {React.Children.map(props.children, (child, i) =>
          React.cloneElement(child, {
            isCurrent: i === childCount - 1
          }))}
      </ol>
    </nav>
  );
}

function BreadcrumbItem(props) {
  let ref = React.useRef(null);
  let { itemProps } = useBreadcrumbItem({
    ...props,
    elementType: 'span'
  }, ref);
  return (
    <li>
      <span
        {...itemProps}
        ref={ref}
        style={{
          color: props.isDisabled
            ? 'var(--gray)'
            : 'var(--blue)',
          textDecoration:
            props.isCurrent || props.isDisabled
              ? 'none'
              : 'underline',
          fontWeight: props.isCurrent ? 'bold' : null,
          cursor: props.isCurrent || props.isDisabled
            ? 'default'
            : 'pointer'
        }}
      >
        {props.children}
      </span>
      {!props.isCurrent &&
        (
          <span
            aria-hidden="true"
            style={{ padding: '0 5px' }}
          >
            {'›'}
          </span>
        )}
    </li>
  );
}

<Breadcrumbs>
  <BreadcrumbItem
    onPress={() => alert('Pressed Folder 1')}
  >
    Folder 1
  </BreadcrumbItem>
  <BreadcrumbItem
    onPress={() => alert('Pressed Folder 2')}
  >
    Folder 2
  </BreadcrumbItem>
  <BreadcrumbItem>Folder 3</BreadcrumbItem>
</Breadcrumbs>
import {
  useBreadcrumbItem,
  useBreadcrumbs
} from 'react-aria';

function Breadcrumbs(
  props
) {
  let { navProps } =
    useBreadcrumbs(
      props
    );
  let childCount = React
    .Children.count(
      props.children
    );

  return (
    <nav {...navProps}>
      <ol
        style={{
          display:
            'flex',
          listStyle:
            'none',
          margin: 0,
          padding: 0
        }}
      >
        {React.Children
          .map(
            props
              .children,
            (child, i) =>
              React
                .cloneElement(
                  child,
                  {
                    isCurrent:
                      i ===
                        childCount -
                          1
                  }
                )
          )}
      </ol>
    </nav>
  );
}

function BreadcrumbItem(
  props
) {
  let ref = React.useRef(
    null
  );
  let { itemProps } =
    useBreadcrumbItem({
      ...props,
      elementType: 'span'
    }, ref);
  return (
    <li>
      <span
        {...itemProps}
        ref={ref}
        style={{
          color:
            props
                .isDisabled
              ? 'var(--gray)'
              : 'var(--blue)',
          textDecoration:
            props
                .isCurrent ||
              props
                .isDisabled
              ? 'none'
              : 'underline',
          fontWeight:
            props
                .isCurrent
              ? 'bold'
              : null,
          cursor:
            props
                .isCurrent ||
              props
                .isDisabled
              ? 'default'
              : 'pointer'
        }}
      >
        {props.children}
      </span>
      {!props
        .isCurrent &&
        (
          <span
            aria-hidden="true"
            style={{
              padding:
                '0 5px'
            }}
          >
            {'›'}
          </span>
        )}
    </li>
  );
}

<Breadcrumbs>
  <BreadcrumbItem
    onPress={() =>
      alert(
        'Pressed Folder 1'
      )}
  >
    Folder 1
  </BreadcrumbItem>
  <BreadcrumbItem
    onPress={() =>
      alert(
        'Pressed Folder 2'
      )}
  >
    Folder 2
  </BreadcrumbItem>
  <BreadcrumbItem>
    Folder 3
  </BreadcrumbItem>
</Breadcrumbs>

To render breadcrumbs that navigate to other pages rather than handle events via onPress, use an <a> element for each BreadcrumbItem. This is the default elementType, so the option can be omitted from useBreadcrumbItem.

function BreadcrumbItem(props) {
  let ref = React.useRef(null);
  let { itemProps } = useBreadcrumbItem(props, ref);
  return (
    <li>
      <a
        {...itemProps}
        ref={ref}
        href={props.href}
        style={{
          color: props.isDisabled ? 'var(--gray)' : 'var(--blue)',
          textDecoration: props.isCurrent || props.isDisabled
            ? 'none'
            : 'underline',
          fontWeight: props.isCurrent ? 'bold' : null,
          cursor: props.isCurrent || props.isDisabled ? 'default' : 'pointer'
        }}
      >
        {props.children}
      </a>
      {!props.isCurrent &&
        <span aria-hidden="true" style={{ padding: '0 5px' }}>{'›'}</span>}
    </li>
  );
}

<Breadcrumbs>
  <BreadcrumbItem href="/">Home</BreadcrumbItem>
  <BreadcrumbItem href="/react-aria">React Aria</BreadcrumbItem>
  <BreadcrumbItem>useBreadcrumbs</BreadcrumbItem>
</Breadcrumbs>
function BreadcrumbItem(props) {
  let ref = React.useRef(null);
  let { itemProps } = useBreadcrumbItem(props, ref);
  return (
    <li>
      <a
        {...itemProps}
        ref={ref}
        href={props.href}
        style={{
          color: props.isDisabled
            ? 'var(--gray)'
            : 'var(--blue)',
          textDecoration:
            props.isCurrent || props.isDisabled
              ? 'none'
              : 'underline',
          fontWeight: props.isCurrent ? 'bold' : null,
          cursor: props.isCurrent || props.isDisabled
            ? 'default'
            : 'pointer'
        }}
      >
        {props.children}
      </a>
      {!props.isCurrent &&
        (
          <span
            aria-hidden="true"
            style={{ padding: '0 5px' }}
          >
            {'›'}
          </span>
        )}
    </li>
  );
}

<Breadcrumbs>
  <BreadcrumbItem href="/">Home</BreadcrumbItem>
  <BreadcrumbItem href="/react-aria">
    React Aria
  </BreadcrumbItem>
  <BreadcrumbItem>useBreadcrumbs</BreadcrumbItem>
</Breadcrumbs>
function BreadcrumbItem(
  props
) {
  let ref = React.useRef(
    null
  );
  let { itemProps } =
    useBreadcrumbItem(
      props,
      ref
    );
  return (
    <li>
      <a
        {...itemProps}
        ref={ref}
        href={props.href}
        style={{
          color:
            props
                .isDisabled
              ? 'var(--gray)'
              : 'var(--blue)',
          textDecoration:
            props
                .isCurrent ||
              props
                .isDisabled
              ? 'none'
              : 'underline',
          fontWeight:
            props
                .isCurrent
              ? 'bold'
              : null,
          cursor:
            props
                .isCurrent ||
              props
                .isDisabled
              ? 'default'
              : 'pointer'
        }}
      >
        {props.children}
      </a>
      {!props
        .isCurrent &&
        (
          <span
            aria-hidden="true"
            style={{
              padding:
                '0 5px'
            }}
          >
            {'›'}
          </span>
        )}
    </li>
  );
}

<Breadcrumbs>
  <BreadcrumbItem href="/">
    Home
  </BreadcrumbItem>
  <BreadcrumbItem href="/react-aria">
    React Aria
  </BreadcrumbItem>
  <BreadcrumbItem>
    useBreadcrumbs
  </BreadcrumbItem>
</Breadcrumbs>

If you use breadcrumbs in the context where a heading would normally be used, you should make sure that the proper elementType for each breadcrumb is communicated to useBreadcrumbItem so that you receive the correct breadcrumbItemProps to spread on your breadcrumb. This can be seen in the example below where we update the BreadcrumbItem component to specify that the current has elementType h3 and all other breadcrumbs are of type a.

function BreadcrumbItem(props) {
  let ref = React.useRef(null);
  let { itemProps } = useBreadcrumbItem({
    ...props,
    elementType: props.isCurrent ? 'h3' : 'a'
  }, ref);
  let breadcrumbContent;
  if (props.isCurrent) {
    breadcrumbContent = (
      <h3
        {...itemProps}
        ref={ref}
        style={{
          margin: 0,
          fontSize: '1em',
          color: 'var(--blue)'
        }}
      >
        {props.children}
      </h3>
    );
  } else {
    breadcrumbContent = (
      <>
        <a
          {...itemProps}
          ref={ref}
          href={props.href}
          style={{
            color: props.isDisabled ? 'var(--gray)' : 'var(--blue)',
            textDecoration: props.isCurrent || props.isDisabled
              ? 'none'
              : 'underline',
            fontWeight: props.isCurrent ? 'bold' : null,
            cursor: props.isCurrent || props.isDisabled ? 'default' : 'pointer'
          }}
        >
          {props.children}
        </a>
        <span aria-hidden="true" style={{ padding: '0 5px' }}>{'›'}</span>
      </>
    );
  }

  return (
    <li>
      {breadcrumbContent}
    </li>
  );
}

<Breadcrumbs>
  <BreadcrumbItem href="/">Home</BreadcrumbItem>
  <BreadcrumbItem href="/react-aria">React Aria</BreadcrumbItem>
  <BreadcrumbItem>useBreadcrumbs</BreadcrumbItem>
</Breadcrumbs>
function BreadcrumbItem(props) {
  let ref = React.useRef(null);
  let { itemProps } = useBreadcrumbItem({
    ...props,
    elementType: props.isCurrent ? 'h3' : 'a'
  }, ref);
  let breadcrumbContent;
  if (props.isCurrent) {
    breadcrumbContent = (
      <h3
        {...itemProps}
        ref={ref}
        style={{
          margin: 0,
          fontSize: '1em',
          color: 'var(--blue)'
        }}
      >
        {props.children}
      </h3>
    );
  } else {
    breadcrumbContent = (
      <>
        <a
          {...itemProps}
          ref={ref}
          href={props.href}
          style={{
            color: props.isDisabled
              ? 'var(--gray)'
              : 'var(--blue)',
            textDecoration:
              props.isCurrent || props.isDisabled
                ? 'none'
                : 'underline',
            fontWeight: props.isCurrent ? 'bold' : null,
            cursor: props.isCurrent || props.isDisabled
              ? 'default'
              : 'pointer'
          }}
        >
          {props.children}
        </a>
        <span
          aria-hidden="true"
          style={{ padding: '0 5px' }}
        >
          {'›'}
        </span>
      </>
    );
  }

  return (
    <li>
      {breadcrumbContent}
    </li>
  );
}

<Breadcrumbs>
  <BreadcrumbItem href="/">Home</BreadcrumbItem>
  <BreadcrumbItem href="/react-aria">
    React Aria
  </BreadcrumbItem>
  <BreadcrumbItem>useBreadcrumbs</BreadcrumbItem>
</Breadcrumbs>
function BreadcrumbItem(
  props
) {
  let ref = React.useRef(
    null
  );
  let { itemProps } =
    useBreadcrumbItem({
      ...props,
      elementType:
        props.isCurrent
          ? 'h3'
          : 'a'
    }, ref);
  let breadcrumbContent;
  if (props.isCurrent) {
    breadcrumbContent = (
      <h3
        {...itemProps}
        ref={ref}
        style={{
          margin: 0,
          fontSize:
            '1em',
          color:
            'var(--blue)'
        }}
      >
        {props.children}
      </h3>
    );
  } else {
    breadcrumbContent = (
      <>
        <a
          {...itemProps}
          ref={ref}
          href={props
            .href}
          style={{
            color:
              props
                  .isDisabled
                ? 'var(--gray)'
                : 'var(--blue)',
            textDecoration:
              props
                  .isCurrent ||
                props
                  .isDisabled
                ? 'none'
                : 'underline',
            fontWeight:
              props
                  .isCurrent
                ? 'bold'
                : null,
            cursor:
              props
                  .isCurrent ||
                props
                  .isDisabled
                ? 'default'
                : 'pointer'
          }}
        >
          {props
            .children}
        </a>
        <span
          aria-hidden="true"
          style={{
            padding:
              '0 5px'
          }}
        >
          {'›'}
        </span>
      </>
    );
  }

  return (
    <li>
      {breadcrumbContent}
    </li>
  );
}

<Breadcrumbs>
  <BreadcrumbItem href="/">
    Home
  </BreadcrumbItem>
  <BreadcrumbItem href="/react-aria">
    React Aria
  </BreadcrumbItem>
  <BreadcrumbItem>
    useBreadcrumbs
  </BreadcrumbItem>
</Breadcrumbs>

Usage#


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

Disabled#

Breadcrumbs can be disabled using the isDisabled prop, passed to each disabled BreadcrumbItem. This indicates that navigation is not currently available. When a breadcrumb is disabled, onPress will not be triggered, navigation will not occur, and links will be marked as aria-disabled for assistive technologies.

<Breadcrumbs>
  <BreadcrumbItem href="/" isDisabled>Home</BreadcrumbItem>
  <BreadcrumbItem href="/react-aria">React Aria</BreadcrumbItem>
  <BreadcrumbItem>useBreadcrumbs</BreadcrumbItem>
</Breadcrumbs>
<Breadcrumbs>
  <BreadcrumbItem href="/" isDisabled>
    Home
  </BreadcrumbItem>
  <BreadcrumbItem href="/react-aria">
    React Aria
  </BreadcrumbItem>
  <BreadcrumbItem>useBreadcrumbs</BreadcrumbItem>
</Breadcrumbs>
<Breadcrumbs>
  <BreadcrumbItem
    href="/"
    isDisabled
  >
    Home
  </BreadcrumbItem>
  <BreadcrumbItem href="/react-aria">
    React Aria
  </BreadcrumbItem>
  <BreadcrumbItem>
    useBreadcrumbs
  </BreadcrumbItem>
</Breadcrumbs>