Side navigation
A highly composable side navigation component that supports nested views.Deprecated
This package is deprecated. Use our new navigation system.
Default
This side navigation example shows all of the components and items composed together.
import React from 'react';
import FilterIcon from '@atlaskit/icon/core/filter';
import FolderClosedIcon from '@atlaskit/icon/core/folder-closed';
import GlobeIcon from '@atlaskit/icon/core/globe';
import LightbulbIcon from '@atlaskit/icon/core/lightbulb';
import PagesIcon from '@atlaskit/icon/core/pages';
import PersonIcon from '@atlaskit/icon/core/person';
import SettingsIcon from '@atlaskit/icon/core/settings';
import { ButtonItem } from '@atlaskit/side-navigation/button-item';
import { LinkItem } from '@atlaskit/side-navigation/link-item';
import { NavigationFooter } from '@atlaskit/side-navigation/navigation-footer';
import { NavigationHeader } from '@atlaskit/side-navigation/navigation-header';
import { NestableNavigationContent } from '@atlaskit/side-navigation/nestable-navigation-content';
import { NestingItem } from '@atlaskit/side-navigation/nesting-item';
import { Section } from '@atlaskit/side-navigation/section';
import { SideNavigation } from '@atlaskit/side-navigation/side-navigation';
import AppFrame from '../common/app-frame';
import SampleFooter from '../common/sample-footer';
import SampleHeader from '../common/sample-header';
const BasicExample = (): React.JSX.Element => {
return (
<AppFrame shouldHideAppBar>
<SideNavigation label="project" testId="side-navigation">
<NavigationHeader>
<SampleHeader />
</NavigationHeader>
<NestableNavigationContent initialStack={[]} testId="nestable-navigation-content">
<Section isList>
<NestingItem
id="filters"
testId="filter-nesting-item"
title="Filters"
iconBefore={<FilterIcon label="" />}
iconAfter={<LightbulbIcon label="" />}
>
<Section>
<ButtonItem>Search work items</ButtonItem>
</Section>
<Section title="Starred" isList>
<ButtonItem>Everything for me</ButtonItem>
<ButtonItem>My open work items</ButtonItem>
<ButtonItem>Reported by me</ButtonItem>
</Section>
<Section hasSeparator title="Other" isList>
<ButtonItem>All work items</ButtonItem>
<ButtonItem>Open work items</ButtonItem>
<ButtonItem>Created recently</ButtonItem>
<ButtonItem>Resolved recently</ButtonItem>
</Section>
<Section hasSeparator>
<ButtonItem>View all filters</ButtonItem>
</Section>
</NestingItem>
<NestingItem
id="queues"
isSelected
title="Queues view"
iconBefore={<PagesIcon label="" />}
>
<Section title="Queues" isList>
<ButtonItem>Untriaged</ButtonItem>
<ButtonItem>My feature work</ButtonItem>
<ButtonItem>My bugfix work</ButtonItem>
<ButtonItem>Signals</ButtonItem>
<ButtonItem>Assigned to me</ButtonItem>
</Section>
<Section hasSeparator>
<ButtonItem>New queue</ButtonItem>
</Section>
</NestingItem>
<NestingItem
id="settings"
iconBefore={<SettingsIcon label="" />}
title="Settings"
testId="settings-nesting-item"
>
<Section>
<NestingItem
iconBefore={<GlobeIcon label="" />}
id="language-menu"
title="Language settings"
>
<Section>
<ButtonItem>Customize</ButtonItem>
<NestingItem id="german-settings" title="German Settings">
<Section>
<ButtonItem>Hallo Welt!</ButtonItem>
</Section>
</NestingItem>
<NestingItem id="english-settings" title="English Settings">
<Section>
<ButtonItem>Hello World!</ButtonItem>
</Section>
</NestingItem>
</Section>
</NestingItem>
</Section>
</NestingItem>
<ButtonItem iconBefore={<FolderClosedIcon label="" />}>Your work</ButtonItem>
<LinkItem href="/" iconBefore={<PersonIcon label="" />}>
Your customers
</LinkItem>
</Section>
</NestableNavigationContent>
<NavigationFooter>
<SampleFooter />
</NavigationFooter>
</SideNavigation>
</AppFrame>
);
};
export default BasicExample;Container
Uses 100% of its parent's height and width, so make sure to place it into an element with explicit values set.
The minimum width of the container is 240px.
import React from 'react';
import { SideNavigation } from '@atlaskit/side-navigation/side-navigation';
import AppFrame from '../common/app-frame';
const ContainerExample = (): React.JSX.Element => {
return (
<AppFrame shouldHideAppBar shouldHideBorder>
<SideNavigation label="project">
<div />
</SideNavigation>
</AppFrame>
);
};
export default ContainerExample;Use NavigationHeader and NavigationFooter to customise the header and footer.
Content
This is used as the container for navigation items. For nested views see the nested navigation example.
import React from 'react';
import { ButtonItem } from '@atlaskit/side-navigation/button-item';
import { Header } from '@atlaskit/side-navigation/header';
import { NavigationContent } from '@atlaskit/side-navigation/navigation-content';
import { NavigationHeader } from '@atlaskit/side-navigation/navigation-header';
import { Section } from '@atlaskit/side-navigation/section';
import { SideNavigation } from '@atlaskit/side-navigation/side-navigation';
import AppFrame from '../common/app-frame';
const ContentExample = (): React.JSX.Element => {
return (
<AppFrame shouldHideAppBar>
<SideNavigation label="project">
<NavigationHeader>
<Header>Design System Project</Header>
</NavigationHeader>
<NavigationContent showTopScrollIndicator>
<Section>
<ButtonItem>Tasks</ButtonItem>
</Section>
</NavigationContent>
</SideNavigation>
</AppFrame>
);
};
export default ContentExample;Section
This is used to separate items into sections. Using the title prop makes a section implicitly
group the items for assistive technology such as screen readers with no extra work required.
import React from 'react';
import { ButtonItem } from '@atlaskit/side-navigation/button-item';
import { HeadingItem } from '@atlaskit/side-navigation/heading-item';
import { Section } from '@atlaskit/side-navigation/section';
const SectionExample = (): React.JSX.Element => {
return (
<div>
<Section title="Planning">
<ButtonItem>Kanban board</ButtonItem>
</Section>
<Section aria-labelledby="actions" hasSeparator>
<HeadingItem id="actions">Actions</HeadingItem>
<ButtonItem>Create work item</ButtonItem>
</Section>
</div>
);
};
export default SectionExample;Use NestableNavigationContent if you need a container for navigation items with nested views.
import React from 'react';
import { ButtonItem } from '@atlaskit/side-navigation/button-item';
import { Header } from '@atlaskit/side-navigation/header';
import { NavigationHeader } from '@atlaskit/side-navigation/navigation-header';
import { NestableNavigationContent } from '@atlaskit/side-navigation/nestable-navigation-content';
import { NestingItem } from '@atlaskit/side-navigation/nesting-item';
import { Section } from '@atlaskit/side-navigation/section';
import { SideNavigation } from '@atlaskit/side-navigation/side-navigation';
import AppFrame from '../common/app-frame';
const NestedExample = (): React.JSX.Element => {
return (
<AppFrame shouldHideAppBar>
<SideNavigation label="project">
<NavigationHeader>
<Header>Designing web navigation</Header>
</NavigationHeader>
<NestableNavigationContent>
<Section>
<NestingItem id="component-menu" title="Navigation components">
<Section>
<ButtonItem>Side navigation</ButtonItem>
</Section>
</NestingItem>
</Section>
</NestableNavigationContent>
</SideNavigation>
</AppFrame>
);
};
export default NestedExample;Granular items that can be rendered as part of the navigation experience.
You must use the Section component to ensure consistent spacing around blocks of items.
Go back item
Use the go back item to provide a customized "go back" button in nested navigations.
import React from 'react';
import { GoBackItem } from '@atlaskit/side-navigation/go-back-item';
import { Section } from '@atlaskit/side-navigation/section';
const ButtonItemExample = (): React.JSX.Element => {
return (
<div>
<Section>
<GoBackItem description="My project name">Back to project</GoBackItem>
</Section>
</div>
);
};
export default ButtonItemExample;Link item
This renders an item wrapped in an anchor tag, useful when you have an item that should navigate to another page using native browser navigation.
For custom SPA transitions use a custom item with the respective router logic, following the custom item example.
import React, { type MouseEvent, useState } from 'react';
import LinkItem from '@atlaskit/menu/link-item';
// Legacy example: this prop type is deprecated with @atlaskit/side-navigation.
// Use @atlaskit/navigation-system for new side navigation.
import { type LinkItemProps } from '@atlaskit/menu/types';
import { Box } from '@atlaskit/primitives/compiled/box';
import ImgIcon from '../common/img-icon';
import koala from '../icons/koala.png';
const useLinkItemComputedProps = (initialSelectedHref?: string) => {
const [currentHref, setCurrentHref] = useState<string | undefined>(initialSelectedHref);
const getComputedProps = ({ href, ...restProps }: LinkItemProps) => ({
href,
...restProps,
isSelected: currentHref === href,
onClick: () => setCurrentHref(href),
});
return getComputedProps;
};
export default (): React.JSX.Element => {
const getComputedProps = useLinkItemComputedProps('#link-item2');
return (
/**
* It is not normally acceptable to add click handlers to non-interactive elements
* as this is an accessibility anti-pattern. However, because this instance is
* for performance reasons (to avoid multiple click handlers) and not creating an
* inaccessible custom element, we can add role="presentation" so that there is
* no negative impacts to assistive technologies.
*/
<Box onClick={(e: MouseEvent) => e.preventDefault()} role="presentation">
<LinkItem {...getComputedProps({ href: '#link-item1' })}>Customer Feedback</LinkItem>
<LinkItem {...getComputedProps({ href: '#link-item2' })}>Customer Feedback</LinkItem>
<LinkItem {...getComputedProps({ href: '#link-item3' })} isDisabled>
Customer Feedback
</LinkItem>
<LinkItem {...getComputedProps({ href: '#link-item4' })} description="Classic service desk">
Customer Feedback
</LinkItem>
<LinkItem
{...getComputedProps({ href: '#link-item5' })}
iconBefore={<ImgIcon src={koala} alt={'A koala'} />}
description="Classic service desk"
>
Customer Feedback
</LinkItem>
<LinkItem {...getComputedProps({ href: 'https://atlassian.design' })} testId="link-item">
Atlassian Design
</LinkItem>
</Box>
);
};Custom item
This handles use cases where a custom router link component is needed.
The custom component receives all overflow props passed to the custom item component. When using TypeScript this will add the custom component props to the root component props type for type safety.
import { cssMap, jsx } from '@compiled/react';
import CustomItem from '@atlaskit/menu/custom-item';
// Legacy example: this prop type is deprecated with @atlaskit/side-navigation.
// Use @atlaskit/navigation-system for new side navigation.
import { type CustomItemComponentProps } from '@atlaskit/menu/types';
import { Box } from '@atlaskit/primitives/compiled/box';
import Slack from '../icons/slack';
type CustomComponentWithHrefProps = CustomItemComponentProps & {
href: string;
};
const CustomComponent = ({ children, href, ...props }: CustomComponentWithHrefProps) => {
return (
<a href={href} {...props}>
{children}
</a>
);
};
const styles = cssMap({
root: {
position: 'relative',
overflow: 'hidden',
userSelect: 'none',
},
interactive: {
'&::before': {
content: '""',
position: 'absolute',
insetInlineStart: 0,
insetBlockStart: 0,
insetBlockEnd: 0,
width: 3,
transform: 'translateX(-1px)',
transition: 'transform 70ms ease-in-out',
backgroundColor: '#4C9AFF',
},
'&:hover::before': {
transform: 'translateX(0)',
},
},
});
const _default: () => JSX.Element = () => (
/**
* It is not normally acceptable to add click handlers to non-interactive elements
* as this is an accessibility anti-pattern. However, because this instance is
* for performance reasons (to avoid multiple click handlers) and not creating an
* inaccessible custom element, we can add role="presentation" so that there is
* no negative impacts to assistive technologies.
*/
<Box onClick={(e: React.MouseEvent) => e.preventDefault()} role="presentation">
<CustomItem
href="/navigation-system"
component={CustomComponent}
css={[styles.root, styles.interactive]}
>
CustomItem
</CustomItem>
<CustomItem
href="/navigation-system-1"
isSelected
component={CustomComponent}
css={[styles.root, styles.interactive]}
>
isSelected CustomItem
</CustomItem>
<CustomItem
href="/navigation-system-2"
isDisabled
component={CustomComponent}
css={styles.root}
>
isDisabled CustomItem
</CustomItem>
<CustomItem
href="/navigation-system-3"
component={CustomComponent}
iconBefore={<Slack />}
css={[styles.root, styles.interactive]}
>
iconBefore CustomItem
</CustomItem>
<CustomItem
href="/navigation-system-4"
component={CustomComponent}
iconBefore={<Slack />}
description="Next-gen software project"
css={[styles.root, styles.interactive]}
>
iconBefore and description CustomItem
</CustomItem>
</Box>
);
export default _default;This renders an item wrapped in a button tag. Use this when an action does something other than navigating to another page.
Loading
Use loading skeletons to reduce the perceived loading time.
import React from 'react';
import { ButtonItem } from '@atlaskit/side-navigation/button-item';
import { Header } from '@atlaskit/side-navigation/header';
import { LoadingItems } from '@atlaskit/side-navigation/loading-items';
import { NavigationContent } from '@atlaskit/side-navigation/navigation-content';
import { NavigationHeader } from '@atlaskit/side-navigation/navigation-header';
import { Section } from '@atlaskit/side-navigation/section';
import { SideNavigation } from '@atlaskit/side-navigation/side-navigation';
import { SkeletonItem } from '@atlaskit/side-navigation/skeleton-item';
import AppFrame from '../common/app-frame';
const LoadingExample = (): React.JSX.Element => {
return (
<AppFrame shouldHideAppBar>
<SideNavigation label="settings">
<NavigationContent>
<LoadingItems
isLoading
fallback={
<>
<NavigationHeader>
<Header description="Next-gen software">Concise Systems</Header>
</NavigationHeader>
<SkeletonItem />
<SkeletonItem hasAvatar />
<SkeletonItem hasIcon isShimmering />
<SkeletonItem isShimmering />
</>
}
>
<Section title="Project settings">
<ButtonItem>Details</ButtonItem>
</Section>
</LoadingItems>
</NavigationContent>
</SideNavigation>
</AppFrame>
);
};
export default LoadingExample;