New side navigation
Allow users to explore content and features through a primary navigation structure with a clear visual hierarchy
#Examples
Ideal for applications with multiple sections and subsections, where a clear visual hierarchy is essential.
The side navigation consists of:
- Links: Clickable text or icons leading to different content areas.
- Icons: Visual representations of navigation items (especially important in the collapsed view).
- Chevrons: Expandable/collapsible indicators for nested navigation levels.
- Levels: Primary (top-level), secondary, and tertiary levels to organize content.
- Collapsed view: Can be used to collapse the navigation in mobile or tablet views.
#Basic usage
This is a basic example, where we have selected an item under "data privacy" and the rest of the settings are at the default values.
Best practices:
- The selected item should be visually distinct.
- Navigation structure should mirror the content's logical hierarchy.
- Ensure the collapsed view uses clear, recognizable icons for each top-level item.
// These states are kept outside the component because you would most likely want to persist
// these in something like local storage to work well will page reloads.
const [selectionId, setSelectionId] = useState<string | null>(
"dataprivacy-personal-data-types-phone-number"
);
const [viewState, setViewState] = useState<NewViewState>();
const [expandedItems, setExpandedItems] = useState<string[]>();
return (
<>
<div style={{ height: "100vh" }}>
<NewSideNavigation
data-observe-key="side-nav"
mainItems={data.sideNavigation}
bottomItems={data.sideBottomNavigation}
selectionId={selectionId}
setSelectionId={setSelectionId}
viewState={viewState}
setViewState={setViewState}
expandedItems={expandedItems}
setExpandedItems={setExpandedItems}
searchHotkey="M"
/>
</div>
</>
);#Falling back to default expansion
This example shows how the component falls back to showing expanded nodes that match with the selection if the expansion sent into the props are not properly showing the selected item. In this case, we select an item under "data privacy", but the expansion is set to showing something under QA. In this case, it will fall back to expanding the the nodes to show the selected item.
- Use Case: This behavior is important when the provided expansion state doesn't align with the user's selection. It ensures the user can easily see the context of their current selection.
- Best Practice: Clearly indicate the selected item, even when the navigation falls back to a default state. This includes a subtle animation to smoothly transition between expansion states.
// These states are kept outside the component because you would most likely want to persist
// these in something like local storage to work well will page reloads.
const [selectionId, setSelectionId] = useState<string | null>(
"dataprivacy-personal-data-types-phone-number"
);
const [viewState, setViewState] = useState<NewViewState>();
const [expandedItems, setExpandedItems] = useState(["qa", "qa-spelling-v2"]);
return (
<>
<div style={{ height: "100vh" }}>
<NewSideNavigation
data-observe-key="side-nav"
mainItems={data.sideNavigation}
bottomItems={data.sideBottomNavigation}
selectionId={selectionId}
setSelectionId={setSelectionId}
viewState={viewState}
setViewState={setViewState}
expandedItems={expandedItems}
setExpandedItems={setExpandedItems}
/>
</div>
</>
);#Using labels
Menu-items can have labels. They are rendered using the Badge component. The label can have an optional icon, text, and tooltip.
- Use Case: To highlight new features, internal use or other contextual information.
- Best Practice:
- Keep labels concise and visually distinct (use the Badge component).
- If using icons, choose universally recognizable ones.
// These states are kept outside the component because you would most likely want to persist
// these in something like local storage to work well will page reloads.
const [selectionId, setSelectionId] = useState<string | null>(null);
const [viewState, setViewState] = useState<NewViewState>();
const [expandedItems, setExpandedItems] = useState<string[]>();
// Copy and wrangle the basic example to show off how to set labels
const nav: MainNavigationItem[] = data.sideNavigation.slice(0, 6);
nav[0].labels = [{ type: "highlight1", icon: <IconFeatureFlag />, tooltip: "Feature Flag Xyz" }];
nav[1].labels = [{ type: "highlight3", text: "Internal", tooltip: "Internal use only" }];
nav[3].children![0].labels = [{ type: "highlight2", text: "Demo", tooltip: "Demo data" }];
nav[4].labels = [
{ type: "warning", text: "Beta", tooltip: "Feature is still work in progress" },
{ type: "highlight1", icon: <IconFeatureFlag />, tooltip: "Feature Flag Xyz" },
];
nav[5].labels = [{ type: "highlight2", text: "Demo", tooltip: "Demo data" }];
return (
<>
<div style={{ height: "35rem" }}>
<NewSideNavigation
data-observe-key="side-nav"
mainItems={nav}
bottomItems={[]}
selectionId={selectionId}
setSelectionId={setSelectionId}
viewState={viewState}
setViewState={setViewState}
expandedItems={expandedItems}
setExpandedItems={setExpandedItems}
/>
</div>
</>
);#Links with onClick handlers
This example has items with both href's but also onclick handlers. The idea is that simple clicks will be handled with the "onClick" handler and "preventDefault" so that the browser doesn't navigate. But if you "open in new tab" click, then the navigation happens.
- Use Case: This pattern is useful when you need to trigger specific actions in addition to, or instead of, navigating to a new page.
- Best Practice:
- Clearly communicate to the user whether a click will result in navigation or a different action.
- If the custom action fails, provide a fallback to ensure the user can still navigate to the destination.
// These states are kept outside the component because you would most likely want to persist
// these in something like local storage to work well will page reloads.
const [selectionId, setSelectionId] = useState<string | null>(null);
const [viewState, setViewState] = useState<NewViewState>();
const [expandedItems, setExpandedItems] = useState<string[]>();
const items: MainNavigationItem[] = [
{
children: [],
defaultChildId: null,
id: "dashboard",
title: "Dashboards",
href: "#",
openNew: false,
icon: <IconProductDashboard />,
observeKey: "side-nav-dashboard",
labels: [],
visible: true,
},
{
children: [
{
children: [],
defaultChildId: null,
id: "dci-overview",
title: "DCI overview",
href: "#",
openNew: false,
icon: <IconProductAds />,
observeKey: "side-nav-dci-overview",
labels: [],
visible: true,
},
{
children: [],
defaultChildId: null,
id: "dci-account-overview",
title: "My sites",
href: "#",
openNew: false,
icon: <IconProductAds />,
observeKey: "side-nav-dci-account-overview",
labels: [],
visible: true,
},
{
children: [],
defaultChildId: null,
id: "dci-sites-progress",
title: "Sites progress",
href: "#",
openNew: false,
icon: <IconProductAds />,
observeKey: "side-nav-dci-sites-progress",
labels: [
{
type: "highlight1",
icon: <IconFeatureFlag />,
tooltip: "The feature is hidden behind the Feature Flag: RMDCISiteProgress",
},
],
visible: true,
},
],
defaultChildId: "dci-overview",
id: "dci",
title: "Digital Certainty Index",
href: "#",
openNew: false,
icon: <IconProductDCI />,
observeKey: "side-nav-dci",
labels: [],
visible: true,
},
];
return (
<>
<div style={{ height: "35rem" }}>
<NewSideNavigation
data-observe-key="side-nav"
mainItems={items}
bottomItems={[]}
selectionId={selectionId}
setSelectionId={setSelectionId}
viewState={viewState}
setViewState={setViewState}
expandedItems={expandedItems}
setExpandedItems={setExpandedItems}
/>
</div>
</>
);#One main nav item and fixed view
If there is only one main nav item in mainItems the sub nav will be shown on load and the "Back to main menu" button will be replaced by a back link that uses the props backLink and backLinkLabel. This can be combined with viewState="fixed" which will hide the view state toggle button to collapse and expand the navigation.
- Use Case: This is perfect for applications with a single primary section and a fixed secondary navigation structure (E.g Page Inspector)
- Best Practice: In this scenario, ensure the back link is clearly visible and labeled. Since the navigation is always visible, make sure it doesn't obscure or overlap with the main content area.
// These states are kept outside the component because you would most likely want to persist
// these in something like local storage to work well will page reloads.
const [selectionId, setSelectionId] = useState<string | null>(null);
const [viewState, setViewState] = useState<NewViewState>("fixed");
const [expandedItems, setExpandedItems] = useState<string[]>();
return (
<>
<div style={{ height: "75vh" }}>
<NewSideNavigation
data-observe-key="side-nav"
mainItems={dataOneMainNavItem}
bottomItems={[]}
selectionId={selectionId}
setSelectionId={setSelectionId}
viewState={viewState}
setViewState={setViewState}
expandedItems={expandedItems}
setExpandedItems={setExpandedItems}
backButtonLabel="Back to main menu"
backButtonUrl="http://www.siteimprove.com"
/>
</div>
</>
);#Properties
#Guidelines
#Best practices
#Do not use when
#Accessibility
Explore detailed guidelines for this component: Accessibility Specifications