Skip to content
shadcn.io
shadcn.io is not affiliated with official shadcn/ui

Shadcn Bottom Navigation for React and Tailwind

Fixed bottom nav for mobile-first apps.

Home

Installation

bunx --bun shadcn@latest add https://kit.dev/r/bottom-navigation.json

Anatomy

BottomNavigation
├── BottomNavigationContent
└── BottomNavigationList
    └── BottomNavigationItem
        ├── BottomNavigationItemIcon
        └── BottomNavigationItemLabel

Usage

import {
  BottomNavigation,
  BottomNavigationContent,
  BottomNavigationList,
  BottomNavigationItem,
  BottomNavigationItemIcon,
  BottomNavigationItemLabel,
} from "@/components/ui/bottom-navigation";
<BottomNavigation defaultValue="home">
  <BottomNavigationContent value="home">{/* page */}</BottomNavigationContent>
  <BottomNavigationList>
    <BottomNavigationItem value="home">
      <BottomNavigationItemIcon>
        <HomeIcon />
      </BottomNavigationItemIcon>
      <BottomNavigationItemLabel>Home</BottomNavigationItemLabel>
    </BottomNavigationItem>
  </BottomNavigationList>
</BottomNavigation>

Keep three to five destinations. The list is position: fixed to the viewport so it stays on screen while the page scrolls. The root’s min-height reserves space so content is not hidden behind the bar.

In docs previews the bar is taken out of fixed with className="static" so it sits in the phone frame. Do not copy static into a full-screen app unless you want an in-flow footer.

Controlled

Use value and onValueChange to control the selected destination.

Root Provider

Use useBottomNavigation with BottomNavigationRootProvider when you need the API outside the tree.

States

Disabled

Examples

Icon only

Omit BottomNavigationItemLabel and set aria-label on each item.

Use asChild on BottomNavigationItem with Link. Pair with navigate on the root for in-app routing.

With badge

Overlay a Badge on the icon for counts.

Guides

Fixed bar in the app

BottomNavigationList is fixed inset-x-0 bottom-0 and includes pb-[env(safe-area-inset-bottom)] for home-indicator inset. The root min-height is 3.5rem plus that inset so the last content is not covered.

Do not add absolute on the list in production — that was only used in older previews. For an embedded mock, use className="static" (or absolute inset-x-0 bottom-0 inside a relative frame).

Tabs

Bottom Navigation is Ark Tabs: same value / defaultValue / onValueChange, same keyboard model (horizontal). Use Tabs for in-page panels with a top tablist. Do not nest one inside the other.

<BottomNavigation
  navigate={(details) => {
    router.push(details.node.href);
  }}
>

API Reference

shadcn.io wraps Ark UI Tabs. Defaults below are shadcn.io values. lazyMount and unmountOnExit default to true (Ark: false).

asChild merges props onto a single child element.

BottomNavigation

Root. Renders a div. Reserves min-height for the fixed list.

PropTypeDefaultDescription
activationMode"automatic" | "manual""automatic"automatic selects on focus. manual selects on click / Enter / Space.
asChildbooleanfalseRender the child element instead of a div.
classNamestring-Class names on the root.
compositeboolean-Treat as composed with other composite widgets.
defaultValuestring | null-Uncontrolled selected item.
deselectableboolean-Allow clicking the active item to clear selection.
hideMode"display-none" | "activity""display-none"How to hide mounted-but-inactive content. activity needs React 19+.
idstring-Unique id for the machine.
idsPartial<{ root: string; trigger: (value: string) => string; list: string; content: (value: string) => string; indicator: string }>-Element ids for composition.
lazyMountbooleantrueMount a content panel the first time it is selected.
loopFocusbooleantrueLoop keyboard focus from last to first item.
navigate(details: NavigateDetails) => void-Called when a link item is chosen. { value, node, href }.
onFocusChange(details: FocusChangeDetails) => void-Called when focus moves. { focusedValue: string }.
onValueChange(details: ValueChangeDetails) => void-Called when the selected item changes. { value: string }.
orientation"horizontal" | "vertical""horizontal"Keep horizontal for a bottom bar.
translations{ listLabel?: string }-Localized strings for the tablist.
unmountOnExitbooleantrueUnmount a panel after it is hidden.
valuestring | null-Controlled selected item.
AttributeDescription
data-slotbottom-navigation
data-scopetabs
data-partroot
data-orientation"horizontal" or "vertical"
data-focusPresent when the list is focused

BottomNavigationList

Fixed bar at the bottom of the viewport. Renders a div with role="tablist".

PropTypeDefaultDescription
aria-labelstring"Main"Accessible name of the tablist.
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the list. Use static to undo fixed in embedded previews.
AttributeDescription
data-slotbottom-navigation-list
data-scopetabs
data-partlist
data-orientation"horizontal" or "vertical"

BottomNavigationItem

One destination. Renders a button with role="tab".

PropTypeDefaultDescription
valuestringrequiredId that must match a BottomNavigationContent when panels are used.
disabledboolean-Disable this item.
asChildbooleanfalseMerge onto a single child (Link, a).
classNamestring-Class names on the item.

Icon-only items need aria-label. When a label is present, icons inside the item render at size-4; icon-only items use size-5. Coarse pointers get a 44×44 hit area.

AttributeDescription
data-slotbottom-navigation-item
data-scopetabs
data-parttrigger
data-selectedPresent when this item is selected
data-disabledPresent when disabled
data-focusPresent when focused
data-orientation"horizontal" or "vertical"
aria-selected"true" or "false"

BottomNavigationItemIcon

Icon slot. Renders a span with aria-hidden.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the icon wrapper.
AttributeDescription
data-slotbottom-navigation-item-icon

BottomNavigationItemLabel

Visible label. Renders a span. Truncates with text-xs.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the label.
AttributeDescription
data-slotbottom-navigation-item-label

BottomNavigationContent

Panel for the selected destination. Renders a div with role="tabpanel". Optional if you drive the page with a router instead of panels.

PropTypeDefaultDescription
valuestringrequiredId that must match a BottomNavigationItem.
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the panel.
AttributeDescription
data-slotbottom-navigation-content
data-scopetabs
data-partcontent
data-selectedPresent when this panel is selected

BottomNavigationRootProvider

Root alternative that takes the API from useBottomNavigation. Renders a div.

PropTypeDefaultDescription
valueUseTabsReturnrequiredReturn value of useBottomNavigation().
asChildbooleanfalseRender the child element instead of a div.
classNamestring-Class names on the root.
hideMode"display-none" | "activity""display-none"How to hide mounted-but-inactive content.
lazyMountbooleantrueMount a panel the first time it is selected.
unmountOnExitbooleantrueUnmount a panel after it is hidden.

Pass defaultValue and other machine options to useBottomNavigation(), not to the provider.

AttributeDescription
data-slotbottom-navigation
data-scopetabs
data-partroot

useBottomNavigation

Creates the tabs API for BottomNavigationRootProvider. Same options as BottomNavigation except layout-only props.

const navigation = useBottomNavigation({ defaultValue: "home" });
navigation.setValue("profile");

BottomNavigationContext / useBottomNavigationContext

Render-prop or hook access to selection. Use inside BottomNavigation or BottomNavigationRootProvider.

PropertyTypeDescription
valuestring | nullSelected item.
focusedValuestring | nullFocused item.
setValue(value: string) => voidSelect an item.
clearValue() => voidClear the selection.
focus() => voidFocus the selected item.
selectNext(fromValue?: string) => voidSelect the next item.
selectPrev(fromValue?: string) => voidSelect the previous item.

BottomNavigationContext children: (context) => ReactNode.

Accessibility

Complies with the Tabs WAI-ARIA design pattern. The list defaults to aria-label="Main". Icon-only items need aria-label on BottomNavigationItem. BottomNavigationItemIcon is aria-hidden.

Keyboard support

KeyDescription
TabMoves into the bar (selected item) or out to the next control.
ArrowRightNext item. Selects it when activationMode is automatic.
ArrowLeftPrevious item.
HomeFirst item.
EndLast item.
Enter / SpaceSelect the focused item (manual activation, and always via pointer).