Build tab, segment, and underline navigation with Nav, NavItem, and NavLink, or from a data array, with sizes, alignment, stacking, and fill layouts.
Overview
<Nav> shares its base markup and styling — the .nav class, active and disabled states — across
plain, tabbed, segmented, and underlined navigation. Swap the variant prop to switch between styles without
changing the surrounding markup.
Basic usage
<Nav> is built with flexbox and provides the foundation every style variant below builds on: some
style overrides for working with lists, link padding for larger hit areas, and disabled styling.
Compose it from <NavItem>/<NavLink> pairs, mark the current link with active, and mark an
unavailable one with disabled.
import { Nav, NavItem, NavLink } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav>
<NavItem>
<NavLink href="#" active>
Active
</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#" disabled>
Disabled
</NavLink>
</NavItem>
</Nav>
)
}The <NavItem>/<NavLink> pairing is optional. Since .nav uses display: flex, rendering
<Nav component="nav"> with direct <NavLink> children behaves the same way, without the extra
markup.
import { Nav, NavLink } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav component="nav">
<NavLink href="#" active>
Active
</NavLink>
<NavLink href="#">Link</NavLink>
<NavLink href="#">Link</NavLink>
<NavLink href="#" disabled>
Disabled
</NavLink>
</Nav>
)
}Data-driven usage
Pass an items array to <Nav> to render navigation from data instead of composing children. Each
item accepts label, href, active, and disabled.
import { Nav } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav
items={[
{ label: 'Home', href: '#', active: true },
{ label: 'Features', href: '#' },
{ label: 'Pricing', href: '#' },
{ label: 'Disabled', href: '#', disabled: true }
]}
/>
)
}The same items array works with the tabs and segments variants below.
import { Nav } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav
variant="tabs"
items={[
{ label: 'Active', href: '#', active: true },
{ label: 'Link', href: '#' },
{ label: 'Another Link', href: '#' },
{ label: 'Disabled', href: '#', disabled: true }
]}
/>
)
}import { Nav } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav
variant="segments"
items={[
{ label: 'Active', href: '#', active: true },
{ label: 'Link', href: '#' },
{ label: 'Another Link', href: '#' },
{ label: 'Disabled', href: '#', disabled: true }
]}
/>
)
}Style variants
Tabs
Set variant="tabs" on the base nav from above to generate a tabbed interface.
import { Nav, NavItem, NavLink } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav variant="tabs">
<NavItem>
<NavLink href="#" active>
Active
</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#" disabled>
Disabled
</NavLink>
</NavItem>
</Nav>
)
}Segments
Set variant="segments" instead for a segmented control, using the same markup. The variant was
called pills before @chassis-ui/css 0.6: variant="pills" still renders it, and is deprecated.
import { Nav, NavItem, NavLink } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav variant="segments">
<NavItem>
<NavLink href="#" active>
Active
</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#" disabled>
Disabled
</NavLink>
</NavItem>
</Nav>
)
}Underline
Set variant="underline" to mark the active link with a line under it.
import { Nav, NavItem, NavLink } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav variant="underline">
<NavItem>
<NavLink href="#" active>
Active
</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#" disabled>
Disabled
</NavLink>
</NavItem>
</Nav>
)
}Sizes
Set size="sm" or size="lg" to scale the padding, gap, and font of the links. Every variant
takes both; with no size, the nav has the medium size.
import { Nav, NavItem, NavLink } from '@chassis-ui/react'
const items = (
<>
<NavItem>
<NavLink href="#" active>
Active
</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
</>
)
export const Example = () => {
return (
<div className="d-flex flex-column align-items-start gap-md">
<Nav variant="segments" size="sm">
{items}
</Nav>
<Nav variant="segments">{items}</Nav>
<Nav variant="segments" size="lg">
{items}
</Nav>
</div>
)
}Alignment and layout
Flexbox utilities control a nav's horizontal alignment, stacking, and how its items fill the available width. Mix and match as needed.
Horizontal alignment
Navs are left-aligned by default. Apply .justify-content-center to center them:
import { Nav, NavItem, NavLink } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav className="justify-content-center">
<NavItem>
<NavLink href="#" active>
Active
</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#" disabled>
Disabled
</NavLink>
</NavItem>
</Nav>
)
}Or .justify-content-end to right-align them:
import { Nav, NavItem, NavLink } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav className="justify-content-end">
<NavItem>
<NavLink href="#" active>
Active
</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#" disabled>
Disabled
</NavLink>
</NavItem>
</Nav>
)
}Vertical stacking
Apply the .flex-column utility to stack nav items vertically instead of horizontally. Use a
responsive variant (e.g. .sm:flex-column) to stack only below a given breakpoint.
import { Nav, NavItem, NavLink } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav className="flex-column">
<NavItem>
<NavLink href="#" active>
Active
</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#" disabled>
Disabled
</NavLink>
</NavItem>
</Nav>
)
}Fill and justify
Two modifier classes extend a nav's contents to the full available width. layout="fill"
proportionately fills all available space with .nav-items — every item occupies the horizontal
space, but not necessarily at equal widths.
import { Nav, NavItem, NavLink } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav variant="segments" layout="fill">
<NavItem>
<NavLink href="#" active>
Active
</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#" disabled>
Disabled
</NavLink>
</NavItem>
</Nav>
)
}layout="justified" instead gives every nav item an equal width, filling the same available space.
import { Nav, NavItem, NavLink } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav variant="segments" layout="justified">
<NavItem>
<NavLink href="#" active>
Active
</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#" disabled>
Disabled
</NavLink>
</NavItem>
</Nav>
)
}Flex utilities
Responsive nav variations can also be built from a series of flexbox utilities directly. This is
more verbose than the layout prop, but offers finer control across breakpoints. The example below
stacks the nav at the lowest breakpoint, then switches to a horizontal layout filling the available
width starting from the sm breakpoint.
import { Nav, NavLink } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav component="nav" variant="segments" className="flex-column sm:flex-row">
<NavLink href="#" active>
Active
</NavLink>
<NavLink href="#">Link</NavLink>
<NavLink href="#">Link</NavLink>
<NavLink href="#" disabled>
Disabled
</NavLink>
</Nav>
)
}Using menus
A <Menu> composes into a nav alongside <NavItem>/<NavLink> pairs, adding a dropdown-style
item to an otherwise flat navigation.
Tabs with menus
import { Nav, NavItem, NavLink, Menu, MenuToggle, MenuList, MenuItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav>
<NavItem>
<NavLink href="#" active>
Active
</NavLink>
</NavItem>
<Menu component="li" className="nav-item">
<MenuToggle color="secondary">Menu button</MenuToggle>
<MenuList>
<MenuItem href="#">Action</MenuItem>
<MenuItem href="#">Another action</MenuItem>
<MenuItem href="#">Something else here</MenuItem>
</MenuList>
</Menu>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#" disabled>
Disabled
</NavLink>
</NavItem>
</Nav>
)
}Segments with menus
import { Nav, NavItem, NavLink, Menu, MenuToggle, MenuList, MenuItem } from '@chassis-ui/react'
export const Example = () => {
return (
<Nav variant="segments">
<NavItem>
<NavLink href="#" active>
Active
</NavLink>
</NavItem>
<Menu component="li" className="nav-item">
<MenuToggle color="secondary">Menu button</MenuToggle>
<MenuList>
<MenuItem href="#">Action</MenuItem>
<MenuItem href="#">Another action</MenuItem>
<MenuItem href="#">Something else here</MenuItem>
</MenuList>
</Menu>
<NavItem>
<NavLink href="#">Link</NavLink>
</NavItem>
<NavItem>
<NavLink href="#" disabled>
Disabled
</NavLink>
</NavItem>
</Nav>
)
}Overflow
A nav wraps onto further lines when its items don't fit. To keep it on one line and move the items
that don't fit into a "More" menu instead, wrap it in a
<NavOverflow>. Set keepVisible on a <NavItem> that
should never move.
Accessibility
When using <Nav> to provide a navigation bar, add role="navigation" to the most logical parent
container, or wrap a <nav> element around the whole navigation — not the <ul> itself, since that
would prevent it from being announced as an actual list by assistive technology. <Nav> renders a
plain list and sets no role of its own.
A navigation bar, even one visually styled as tabs via variant="tabs", shouldn't be given
role="tablist", role="tab", or role="tabpanel" attributes. Those are only appropriate for
dynamic tabbed interfaces, as described in the
WAI ARIA Authoring Practices.
<Nav> itself only renders the nav/nav-tabs/nav-segments styling and has no notion of a selected
panel to wire up — reach for Tabs instead, which handles the full
role="tablist"/role="tab"/role="tabpanel" + aria-selected/aria-controls/aria-labelledby
pattern, keyboard navigation, and animated panel switching.
API
Nav
| Prop | Type | Default |
|---|---|---|
asChild? | boolean | — |
Render the single child element in place of this component's own element, with this
component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log
in</Link></Button>. The alternative to component for React Server Components — an element
can be passed from the server to this package's client components, a component reference
(component={Link}) can't. | ||
className? | string | — |
| A string of all className you want applied to the base component. | ||
component? | ElementType | — |
| Component used for the root node. Either a string to use a HTML element or a component. | ||
items? | NavItemDef[] | — |
| Array of nav item definitions for data-driven rendering. When provided, children are ignored. | ||
layout? | "fill" | "justified" | — |
| Specify a layout type for component. | ||
ref? | any | — |
size? | "sm" | "lg" | — |
| Size the component sm or lg. | ||
variant? | { 'tabs' | 'segments' | 'underline' | 'pills' } | — |
Set the nav variant to tabs, to segments (a segmented control) or to underline, which
underlines the active link. 'pills' is the former name of 'segments': deprecated, it
renders the same. | ||
NavItemDef
Shape of each entry in Nav's items array (data-driven rendering).
| Prop | Type | Default |
|---|---|---|
active? | boolean | — |
| Marks the item as active. | ||
disabled? | boolean | — |
| Marks the item as disabled. | ||
href? | string | — |
| URL for the nav link. | ||
label | ReactNode | — |
| Label content for the nav item. | ||
NavItem
| Prop | Type | Default |
|---|---|---|
active? | boolean | — |
| Toggle the active state for the component. | ||
asChild? | boolean | — |
Render the single child element in place of this component's own element, with this
component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log
in</Link></Button>. The alternative to component for React Server Components — an element
can be passed from the server to this package's client components, a component reference
(component={Link}) can't. | ||
className? | string | — |
| A string of all className you want applied to the component. | ||
color? | ContextColor | — |
Sets the link color to one of Chassis context colors, including its interactive
(:hover/:focus/:active/:visited) states. | ||
component? | ElementType | — |
| Component used for the root node. Either a string to use a HTML element or a component. | ||
disabled? | boolean | — |
| Toggle the disabled state for the component. | ||
href? | string | — |
The href attribute specifies the URL of the page the link goes to. Reaches an <a> or a
component reference (a router link); dropped, with a warning in development, when
component is another element. Declared explicitly here — rather than left to flow through
generically from whatever C is — so consumers that wrap Link (MenuItem, ListItem,
NavLink) can read it with a concrete type regardless of component. | ||
iconLink? | boolean | — |
Aligns a leading or trailing icon with the link text using flexbox, with a gap between
them and an offset underline. Icons need to be passed as children alongside the text.
Named iconLink rather than icon to avoid colliding with components (e.g. MenuItem)
that already have their own, differently-typed icon prop for the icon content itself. | ||
keepVisible? | boolean | — |
Keeps the item in the list inside a NavOverflow, whatever the width. | ||
onClick? | MouseEventHandler<HTMLElement> | — |
| Fires on click. | ||
ref? | Ref<HTMLLIElement> | — |
reset? | boolean | — |
| Removes the foreground color override, so the link inherits its color from the nearest ancestor instead of the default link color. | ||
stretched? | boolean | — |
Expands the link's click target to fill its positioned ancestor (the nearest ancestor with
a position other than static). | ||
type? | "reset" | "button" | "submit" | — |
Specifies the type of button. Only applies when component="button". Different browsers may
use different default types for the <button> element, so always specify it explicitly. | ||
NavLink
| Prop | Type | Default |
|---|---|---|
active? | boolean | — |
| Toggle the active state for the component. | ||
asChild? | boolean | — |
Render the single child element in place of this component's own element, with this
component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log
in</Link></Button>. The alternative to component for React Server Components — an element
can be passed from the server to this package's client components, a component reference
(component={Link}) can't. | ||
className? | string | — |
| A string of all className you want applied to the component. | ||
color? | ContextColor | — |
Sets the link color to one of Chassis context colors, including its interactive
(:hover/:focus/:active/:visited) states. | ||
component? | ElementType | — |
| Component used for the root node. Either a string to use a HTML element or a component. | ||
disabled? | boolean | — |
| Toggle the disabled state for the component. | ||
href? | string | — |
The href attribute specifies the URL of the page the link goes to. Reaches an <a> or a
component reference (a router link); dropped, with a warning in development, when
component is another element. Declared explicitly here — rather than left to flow through
generically from whatever C is — so consumers that wrap Link (MenuItem, ListItem,
NavLink) can read it with a concrete type regardless of component. | ||
iconLink? | boolean | — |
Aligns a leading or trailing icon with the link text using flexbox, with a gap between
them and an offset underline. Icons need to be passed as children alongside the text.
Named iconLink rather than icon to avoid colliding with components (e.g. MenuItem)
that already have their own, differently-typed icon prop for the icon content itself. | ||
onClick? | MouseEventHandler<HTMLElement> | — |
| Fires on click. | ||
ref? | any | — |
reset? | boolean | — |
| Removes the foreground color override, so the link inherits its color from the nearest ancestor instead of the default link color. | ||
stretched? | boolean | — |
Expands the link's click target to fill its positioned ancestor (the nearest ancestor with
a position other than static). | ||
type? | "button" | "reset" | "submit" | — |
Specifies the type of button. Only applies when component="button". Different browsers may
use different default types for the <button> element, so always specify it explicitly. | ||