Nav
Sidebar navigation with grouped items, icons, and trailing slots
import {
Home01,
Inbox01,
Settings01,
Users01,
} from '@untitledui-pro/icons/solid';
import { Badge } from '@/components/badge';
import { Nav } from '@/components/nav';
export default function NavPreview() {
return (
<div className="w-64 rounded-xl border border-border bg-background">
<Nav>
<Nav.Group label="Workspace">
<Nav.Item href="#" active>
<Nav.Icon>
<Home01 />
</Nav.Icon>
<Nav.Label>Home</Nav.Label>
</Nav.Item>
<Nav.Item href="#">
<Nav.Icon>
<Inbox01 />
</Nav.Icon>
<Nav.Label>Inbox</Nav.Label>
<Nav.Trailing>
<Badge size="xs" variant="neutral">
12
</Badge>
</Nav.Trailing>
</Nav.Item>
<Nav.Item href="#">
<Nav.Icon>
<Users01 />
</Nav.Icon>
<Nav.Label>Team</Nav.Label>
</Nav.Item>
</Nav.Group>
<Nav.Group label="Settings">
<Nav.Item href="#">
<Nav.Icon>
<Settings01 />
</Nav.Icon>
<Nav.Label>Preferences</Nav.Label>
</Nav.Item>
</Nav.Group>
</Nav>
</div>
);
} Dependencies
Source Code
import type { VariantProps } from 'cva';
import { Slot } from '@/components/slot';
import { cn, cva } from '@/lib/utils/classnames';
const Nav = ({
ref,
className,
...rest
}: React.ComponentPropsWithRef<'nav'>) => (
<nav
ref={ref}
className={cn('flex flex-col gap-6 p-3', className)}
{...rest}
/>
);
interface NavGroupProps extends React.ComponentPropsWithRef<'div'> {
label?: React.ReactNode;
}
const NavGroup = ({
ref,
className,
label,
children,
...rest
}: NavGroupProps) => (
<div ref={ref} className={cn('flex flex-col gap-1', className)} {...rest}>
{label && (
<div className="px-2 pt-1 pb-1 font-medium text-foreground-secondary text-xs uppercase tracking-wider">
{label}
</div>
)}
{children}
</div>
);
const navItemStyle = cva({
base: [
'group relative flex items-center gap-2.5 rounded-lg px-2.5 py-1.5 font-medium text-foreground-secondary text-sm',
'transition duration-(--duration-hover) ease-(--ease)',
'hover:bg-foreground/5 hover:text-foreground',
'focus-visible:ring-(length:--ring-width) ring-ring focus-visible:outline-none',
'data-[active]:bg-foreground/8 data-[active]:text-foreground',
'[&>[data-nav-icon]]:size-4 [&>[data-nav-icon]]:shrink-0',
],
variants: {
size: {
sm: 'h-7 text-sm',
md: 'h-8 text-sm',
lg: 'h-9 text-base',
},
},
defaultVariants: {
size: 'md',
},
});
interface NavItemProps
extends React.ComponentPropsWithRef<'a'>,
VariantProps<typeof navItemStyle> {
asChild?: boolean;
active?: boolean;
}
const NavItem = ({
ref,
className,
size,
asChild,
active,
...rest
}: NavItemProps) => {
const Comp = asChild ? Slot : 'a';
return (
<Comp
ref={ref}
data-active={active ? '' : undefined}
className={cn(navItemStyle({ size }), className)}
{...rest}
/>
);
};
const NavIcon = ({
ref,
className,
...rest
}: React.ComponentPropsWithRef<'span'>) => (
<span
ref={ref}
data-nav-icon
aria-hidden
className={cn('inline-flex items-center justify-center', className)}
{...rest}
/>
);
const NavLabel = ({
ref,
className,
...rest
}: React.ComponentPropsWithRef<'span'>) => (
<span
ref={ref}
className={cn('min-w-0 flex-1 truncate', className)}
{...rest}
/>
);
const NavTrailing = ({
ref,
className,
...rest
}: React.ComponentPropsWithRef<'span'>) => (
<span
ref={ref}
className={cn(
'ml-auto inline-flex shrink-0 items-center gap-1 text-foreground-secondary',
className
)}
{...rest}
/>
);
const CompoundNav = Object.assign(Nav, {
Group: NavGroup,
Item: NavItem,
Icon: NavIcon,
Label: NavLabel,
Trailing: NavTrailing,
});
export type { NavGroupProps, NavItemProps };
export { CompoundNav as Nav, navItemStyle }; Server component. No
'use client'— navigation markup compound; forwards props, no hooks or own handlers.
Nav is the navigation list used inside an AppShell.Sidebar. It composes
groups of Nav.Item rows with optional icons, labels, and trailing content
(badges, kbd hints, counters).
Anatomy
<Nav>
<Nav.Group label="Workspace">
<Nav.Item href="/" active>
<Nav.Icon>{icon}</Nav.Icon>
<Nav.Label>Inbox</Nav.Label>
<Nav.Trailing>{badge}</Nav.Trailing>
</Nav.Item>
</Nav.Group>
</Nav>
API Reference
Nav
Extends the nav element.
Nav.Group
Extends the div element.
| Prop | Default | Type |
|---|---|---|
label | - | ReactNode |
Nav.Item
Extends the a element.
| Prop | Default | Type |
|---|---|---|
active | - | boolean |
size | "md" | "sm""md""lg" |
asChild | - | boolean |
Nav.Icon
Extends the span element.
Nav.Label
Extends the span element.
Nav.Trailing
Extends the span element.
Examples
Default
import {
Home01,
Inbox01,
Settings01,
Users01,
} from '@untitledui-pro/icons/solid';
import { Badge } from '@/components/badge';
import { Nav } from '@/components/nav';
export default function NavPreview() {
return (
<div className="w-64 rounded-xl border border-border bg-background">
<Nav>
<Nav.Group label="Workspace">
<Nav.Item href="#" active>
<Nav.Icon>
<Home01 />
</Nav.Icon>
<Nav.Label>Home</Nav.Label>
</Nav.Item>
<Nav.Item href="#">
<Nav.Icon>
<Inbox01 />
</Nav.Icon>
<Nav.Label>Inbox</Nav.Label>
<Nav.Trailing>
<Badge size="xs" variant="neutral">
12
</Badge>
</Nav.Trailing>
</Nav.Item>
<Nav.Item href="#">
<Nav.Icon>
<Users01 />
</Nav.Icon>
<Nav.Label>Team</Nav.Label>
</Nav.Item>
</Nav.Group>
<Nav.Group label="Settings">
<Nav.Item href="#">
<Nav.Icon>
<Settings01 />
</Nav.Icon>
<Nav.Label>Preferences</Nav.Label>
</Nav.Item>
</Nav.Group>
</Nav>
</div>
);
} Previous
Modal
Next
Number Input