useBreadcrumbs
Provides the behavior and accessibility implementation for a breadcrumbs component. Breadcrumbs display a heirarchy of links to the current page or resource in an application.
install | yarn add @react-aria/breadcrumbs |
---|---|
version | 3.1.10 |
usage | import {useBreadcrumbs, useBreadcrumbItem} from '@react-aria/breadcrumbs' |
API#
useBreadcrumbs<T>(
(props: AriaBreadcrumbsProps<T>
)): BreadcrumbsAria
useBreadcrumbItem(
(props: AriaBreadcrumbItemProps,
, ref: RefObject<HTMLElement>
)): 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#
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:
Name | Type | Description |
navProps | HTMLAttributes<HTMLElement> | Props for the breadcrumbs navigation element. |
useBreadcrumbItem
returns props to spread onto the individual breadcrumb links:
Name | Type | Description |
itemProps | HTMLAttributes<HTMLElement> | Props 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/breadcrumbs';
function Breadcrumbs(props) {
let { navProps } = useBreadcrumbs(props);
let children = React.Children.toArray(props.children);
return (
<nav {...navProps}>
<ol style={{ display: 'flex', listStyle: 'none', margin: 0, padding: 0 }}>
{children.map((child, i) =>
React.cloneElement(child, { isCurrent: i === children.length - 1 })
)}
</ol>
</nav>
);
}
function BreadcrumbItem(props) {
let ref = React.useRef();
let { itemProps } = useBreadcrumbItem({ ...props, elementType: 'span' }, ref);
return (
<li>
<span
{...itemProps}
ref={ref}
style={{
color: 'var(--blue)',
textDecoration: props.isCurrent ? null : 'underline',
fontWeight: props.isCurrent ? 'bold' : null,
cursor: props.isCurrent ? '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/breadcrumbs';
function Breadcrumbs(props) {
let { navProps } = useBreadcrumbs(props);
let children = React.Children.toArray(props.children);
return (
<nav {...navProps}>
<ol
style={{
display: 'flex',
listStyle: 'none',
margin: 0,
padding: 0
}}
>
{children.map((child, i) =>
React.cloneElement(child, {
isCurrent: i === children.length - 1
})
)}
</ol>
</nav>
);
}
function BreadcrumbItem(props) {
let ref = React.useRef();
let { itemProps } = useBreadcrumbItem({
...props,
elementType: 'span'
}, ref);
return (
<li>
<span
{...itemProps}
ref={ref}
style={{
color: 'var(--blue)',
textDecoration: props.isCurrent
? null
: 'underline',
fontWeight: props.isCurrent ? 'bold' : null,
cursor: props.isCurrent ? '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/breadcrumbs';
function Breadcrumbs(
props
) {
let { navProps } =
useBreadcrumbs(
props
);
let children = React
.Children.toArray(
props.children
);
return (
<nav {...navProps}>
<ol
style={{
display:
'flex',
listStyle:
'none',
margin: 0,
padding: 0
}}
>
{children.map((
child,
i
) =>
React
.cloneElement(
child,
{
isCurrent:
i ===
children
.length -
1
}
)
)}
</ol>
</nav>
);
}
function BreadcrumbItem(
props
) {
let ref = React
.useRef();
let { itemProps } =
useBreadcrumbItem({
...props,
elementType: 'span'
}, ref);
return (
<li>
<span
{...itemProps}
ref={ref}
style={{
color:
'var(--blue)',
textDecoration:
props
.isCurrent
? null
: 'underline',
fontWeight:
props
.isCurrent
? 'bold'
: null,
cursor:
props
.isCurrent
? '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>
Navigation links#
This example is similar to the previous one, except the individual breadcrumbs are native navigation links to other pages rather than handled by JavaScript.
function Breadcrumbs(props) {
// Same as in the previous example...
}
function BreadcrumbItem(props) {
let ref = React.useRef();
let {itemProps} = useBreadcrumbItem(props, ref);
return (
<li>
<a
{...itemProps}
ref={ref}
href={props.href}
style={{
color: 'var(--blue)',
fontWeight: props.isCurrent ? 'bold' : null,
cursor: props.isCurrent ? '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 Breadcrumbs(props) {
// Same as in the previous example...
}
function BreadcrumbItem(props) {
let ref = React.useRef();
let { itemProps } = useBreadcrumbItem(props, ref);
return (
<li>
<a
{...itemProps}
ref={ref}
href={props.href}
style={{
color: 'var(--blue)',
fontWeight: props.isCurrent ? 'bold' : null,
cursor: props.isCurrent ? '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 Breadcrumbs(
props
) {
// Same as in the previous example...
}
function BreadcrumbItem(
props
) {
let ref = React
.useRef();
let { itemProps } =
useBreadcrumbItem(
props,
ref
);
return (
<li>
<a
{...itemProps}
ref={ref}
href={props.href}
style={{
color:
'var(--blue)',
fontWeight:
props
.isCurrent
? 'bold'
: null,
cursor:
props
.isCurrent
? '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>
Breadcrumbs as a heading#
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 specify that the last breadcrumb has elementType h3
and all other
breadcrumbs are of type a
.
function Breadcrumbs(props) {
let {navProps} = useBreadcrumbs(props);
let children = React.Children.toArray(props.children);
return (
<nav {...navProps}>
<ol style={{display: 'flex', listStyle: 'none', margin: 0, padding: 0}}>
{children.map((child, i) =>
React.cloneElement(child, {
isCurrent: i === children.length - 1,
elementType: i === children.length - 1 ? 'h3' : 'a'
})
)}
</ol>
</nav>
);
}
function BreadcrumbItem(props) {
let ref = React.useRef();
let {itemProps} = useBreadcrumbItem(props, 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)',
fontWeight: props.isCurrent ? 'bold' : null,
cursor: props.isCurrent ? '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 Breadcrumbs(props) {
let { navProps } = useBreadcrumbs(props);
let children = React.Children.toArray(props.children);
return (
<nav {...navProps}>
<ol
style={{
display: 'flex',
listStyle: 'none',
margin: 0,
padding: 0
}}
>
{children.map((child, i) =>
React.cloneElement(child, {
isCurrent: i === children.length - 1,
elementType: i === children.length - 1
? 'h3'
: 'a'
})
)}
</ol>
</nav>
);
}
function BreadcrumbItem(props) {
let ref = React.useRef();
let { itemProps } = useBreadcrumbItem(props, 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)',
fontWeight: props.isCurrent ? 'bold' : null,
cursor: props.isCurrent ? '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 Breadcrumbs(
props
) {
let { navProps } =
useBreadcrumbs(
props
);
let children = React
.Children.toArray(
props.children
);
return (
<nav {...navProps}>
<ol
style={{
display:
'flex',
listStyle:
'none',
margin: 0,
padding: 0
}}
>
{children.map((
child,
i
) =>
React
.cloneElement(
child,
{
isCurrent:
i ===
children
.length -
1,
elementType:
i ===
children
.length -
1
? 'h3'
: 'a'
}
)
)}
</ol>
</nav>
);
}
function BreadcrumbItem(
props
) {
let ref = React
.useRef();
let { itemProps } =
useBreadcrumbItem(
props,
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)',
fontWeight:
props
.isCurrent
? 'bold'
: null,
cursor:
props
.isCurrent
? '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>