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.jsonpnpm dlx shadcn@latest add https://kit.dev/r/bottom-navigation.jsonnpx shadcn@latest add https://kit.dev/r/bottom-navigation.jsonyarn shadcn@latest add https://kit.dev/r/bottom-navigation.jsonThis component depends on Tabs. Install it first if you haven't already.
Install the following dependencies:
bun add @ark-ui/reactpnpm add @ark-ui/reactnpm install @ark-ui/reactyarn add @ark-ui/reactCopy and paste the following code into your project.
"use client";
import { ark } from "@ark-ui/react/factory";
import {
Tabs as ArkTabs,
useTabs as useArkTabs,
useTabsContext as useArkTabsContext,
} from "@ark-ui/react/tabs";
import type React from "react";
import { cn } from "@/lib/utils";
export const useBottomNavigation = useArkTabs;
export const useBottomNavigationContext = useArkTabsContext;
export const BottomNavigationContext = ArkTabs.Context;
export const BottomNavigation = (
props: React.ComponentProps<typeof ArkTabs.Root>
) => {
const { lazyMount = true, unmountOnExit = true, className, ...rest } = props;
return (
<ArkTabs.Root
className={cn(
"w-full",
"min-h-[calc(var(--spacing)*14+env(safe-area-inset-bottom,0px))]",
className
)}
data-slot="bottom-navigation"
lazyMount={lazyMount}
unmountOnExit={unmountOnExit}
{...rest}
/>
);
};
export const BottomNavigationRootProvider = (
props: React.ComponentProps<typeof ArkTabs.RootProvider>
) => {
const { lazyMount = true, unmountOnExit = true, className, ...rest } = props;
return (
<ArkTabs.RootProvider
className={cn(
"w-full",
"min-h-[calc(var(--spacing)*14+env(safe-area-inset-bottom,0px))]",
className
)}
data-slot="bottom-navigation"
lazyMount={lazyMount}
unmountOnExit={unmountOnExit}
{...rest}
/>
);
};
export const BottomNavigationList = (
props: React.ComponentProps<typeof ArkTabs.List>
) => {
const { "aria-label": ariaLabel = "Main", className, ...rest } = props;
return (
<ArkTabs.List
aria-label={ariaLabel}
className={cn(
"fixed inset-x-0 bottom-0 z-10",
"flex w-full items-center justify-around",
"min-h-14 shrink-0",
"border-t bg-background/60 backdrop-blur-sm",
"pb-[env(safe-area-inset-bottom,0px)]",
className
)}
data-slot="bottom-navigation-list"
{...rest}
/>
);
};
export const BottomNavigationItem = (
props: React.ComponentProps<typeof ArkTabs.Trigger>
) => {
const { className, ...rest } = props;
return (
<ArkTabs.Trigger
className={cn(
"relative",
"min-w-0",
"flex flex-1 flex-col items-center justify-center gap-0.5",
"p-2",
"text-muted-foreground",
"cursor-pointer",
"transition-colors",
"hover:text-foreground",
"aria-selected:text-primary",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
"data-disabled:pointer-events-none data-disabled:opacity-64",
"[&_svg:not([class*='size-'])]:size-5 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"has-data-[slot=bottom-navigation-item-label]:[&_svg:not([class*='size-'])]:size-4",
"pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11",
"motion-reduce:transition-none!",
className
)}
data-slot="bottom-navigation-item"
{...rest}
/>
);
};
export const BottomNavigationItemIcon = (
props: React.ComponentProps<typeof ark.span>
) => {
const { className, ...rest } = props;
return (
<ark.span
aria-hidden
className={cn("flex items-center justify-center", className)}
data-slot="bottom-navigation-item-icon"
{...rest}
/>
);
};
export const BottomNavigationItemLabel = (
props: React.ComponentProps<typeof ark.span>
) => {
const { className, ...rest } = props;
return (
<ark.span
className={cn("truncate font-medium text-xs", className)}
data-slot="bottom-navigation-item-label"
{...rest}
/>
);
};
export const BottomNavigationContent = (
props: React.ComponentProps<typeof ArkTabs.Content>
) => {
const { className, ...rest } = props;
return (
<ArkTabs.Content
className={cn("min-h-0 flex-1 outline-none", className)}
data-slot="bottom-navigation-content"
{...rest}
/>
);
};Update the import paths to match your project setup.
Anatomy
BottomNavigation
├── BottomNavigationList
├── BottomNavigationItem
├── BottomNavigationContent
└── BottomNavigationIndicatorUsage
import { BellIcon, HomeIcon, SearchIcon, UserIcon } from "lucide-react";
import {
BottomNavigation,
BottomNavigationContent,
BottomNavigationItem,
BottomNavigationItemIcon,
BottomNavigationItemLabel,
BottomNavigationList,
} from "@/components/ui/bottom-navigation";const items = [
{ icon: HomeIcon, label: "Home", value: "home" },
{ icon: SearchIcon, label: "Search", value: "search" },
{ icon: BellIcon, label: "News", value: "news" },
{ icon: UserIcon, label: "Profile", value: "profile" },
];
const Example = () => (
<div className="relative flex h-80 w-full max-w-xs flex-col overflow-hidden rounded-xl border bg-background shadow-lg/5">
<BottomNavigation
className="flex h-full min-h-0 flex-col"
defaultValue="home"
>
{items.map((item) => (
<BottomNavigationContent
className="flex min-h-0 flex-1 items-center justify-center bg-muted"
key={item.value}
value={item.value}
>
<span className="text-muted-foreground text-sm">{item.label}</span>
</BottomNavigationContent>
))}
<BottomNavigationList className="static bg-background">
{items.map((item) => (
<BottomNavigationItem key={item.value} value={item.value}>
<BottomNavigationItemIcon>
<item.icon />
</BottomNavigationItemIcon>
<BottomNavigationItemLabel>{item.label}</BottomNavigationItemLabel>
</BottomNavigationItem>
))}
</BottomNavigationList>
</BottomNavigation>
</div>
);Accessibility
| Key | Description |
|---|---|
ArrowDown | Move to the next item when orientation is "vertical". |
ArrowUp | Move to the previous item when orientation is "vertical". |
ArrowLeft | Move to the previous item when orientation is "horizontal". |
ArrowRight | Move to the next item when orientation is "horizontal". |
Home | Go to the start. |
End | Go to the end. |
Enter | Activate the focused control. |
Space | Activate the focused control. |
Demos
Controlled
Disabled
Icon Only
Root Provider
With Badge
With Links
API Reference
BottomNavigation
div
PropType
AttributeType
CSS variableType
BottomNavigationList
PropType
AttributeType
BottomNavigationItem
PropType
AttributeType
BottomNavigationContent
PropType
AttributeType
BottomNavigationItemIcon
span
PropType
AttributeType
BottomNavigationItemLabel
span
PropType
AttributeType
BottomNavigationRootProvider
div
PropType
AttributeType
CSS variableType