Skip to content
React NativeBetaMobile only

BottomNavigation

Top-level destinations at the bottom of the app, Material 3 style: icon in a pill, labels and badges.

Beta. React Native support is in beta. APIs may change or break between prereleases (npm tag native).

On this page
Web preview — real device may differ
tsx
import { useState } from "react";
import { ScrollView, Text, View } from "react-native";
import { BottomNavigation, type BottomNavigationItem } from "@/component-lib/bottom-navigation";
import { useKitTheme } from "@/component-lib/shared/native";
import { Section } from "./layout"; // Section and Row: page layout for the demo
import { FakeRows, MiniScreen, TextIcon } from "./mobileFrame";
 
const icon = (glyph: string) => ({ color }: { color: string }) => <TextIcon glyph={glyph} color={color} />;
 
const ITEMS: BottomNavigationItem[] = [
  { key: "home", label: "Home", icon: icon("⌂") },
  { key: "invoices", label: "Invoices", icon: icon("▤"), badge: 3 },
  { key: "payments", label: "Payments", icon: icon("₹"), badge: 128 },
  { key: "profile", label: "Profile", icon: icon("◉"), badge: true },
];
 
export function BottomNavigationDemo() {
  const { tokens } = useKitTheme();
  const [tab, setTab] = useState("invoices");
  const [reselects, setReselects] = useState(0);
  return (
    <>
      <Section title="BottomNavigation · badges, reselect">
        <MiniScreen>
          <View style={{ flex: 1 }}>
            <View style={{ padding: 16 }}>
              <Text style={{ color: tokens.fg, fontSize: 20, fontWeight: "700" }}>
                {ITEMS.find((i) => i.key === tab)?.label}
              </Text>
              <Text style={{ color: tokens.muted }}>Tap the active tab again: reselected {reselects}×</Text>
            </View>
            <ScrollView nestedScrollEnabled>
              <FakeRows count={6} />
            </ScrollView>
          </View>
          <BottomNavigation
            safeArea={false}
            items={ITEMS}
            value={tab}
            onValueChange={setTab}
            onReselect={() => setReselects((n) => n + 1)}
          />
        </MiniScreen>
      </Section>
 
      <Section title="BottomNavigation · label only when active, disabled item">
        <MiniScreen height={64}>
          <BottomNavigation
            safeArea={false}
            labelVisibility="active"
            items={[...ITEMS.slice(0, 3), { key: "admin", label: "Admin", icon: icon("⚙"), disabled: true }]}
          />
        </MiniScreen>
      </Section>
    </>
  );
}

Install

npx @grampro/kit@native add BottomNavigation

Usage

tsx
import { BottomNavigation } from "@/component-lib/bottom-navigation";
 
<BottomNavigation
  value={tab}
  onValueChange={setTab}
  items={[
    { key: "home", label: "Home", icon: ({ color }) => <HomeIcon color={color} /> },
    { key: "invoices", label: "Invoices", badge: 3 },
    { key: "settings", label: "Settings" },
  ]}
/>

Props

BottomNavigationProps

PropTypeDescription
items (required)BottomNavigationItem[]
valuestringThe active item's key.
defaultValuestring
onValueChange(key: string) => void
onReselect(key: string) => voidTapping the active item again: scroll to top, refresh.
labelVisibility"always" | "active" | "never"Default always.
safeAreabooleanPad the bottom by the safe-area inset (home indicator, gesture bar). Default true.
styleStyleProp<ViewStyle>
stylesPartial<{ root: StyleProp<ViewStyle>; item: StyleProp<ViewStyle>; indicator: StyleProp<…
localeTextPartial<BottomNavigationLocaleText>

Notes

  • Items are role="tab" with aria-selected; a badge is spoken with its label ("Invoices, 3 new").
  • onReselect fires when the active item is tapped again, for scroll-to-top or refresh.
  • Pads its bottom by KitProvider's insets.bottom.
  • Mobile only: there is no web version of this component.