Description:
react-native-cupertino-tabs is a React Native component that renders an iOS UISegmentedControl as a tab bar.
The package runs on the React Native New Architecture and requires react-native-nitro-modules. iOS renders the native control. Android, web, and SSR render an empty View with the requested size.
Preview

Features
- Native iOS rendering through a compound React API.
- SF Symbol names plus bundled or remote image sources for tab icons.
- Controlled selection with
valueor initial selection withdefaultValue. onValueChangereturns the selected value and its index.- Row and column layouts through
tabBarOptions.direction. - Track colors, selection pill color, active and inactive tints, spacing, and icon size.
- Drag-to-select behavior and selection haptics from the native control.
Use cases
- Music library screens can use SF Symbols and labels in a native iOS selector.
- In settings,
direction: "column"puts icons and labels into a vertically stacked tab layout. - A shared React Native codebase can keep its tab state in JavaScript and render this native selector behind an iOS platform check.
- Product-specific artwork fits through custom colors and per-tab accessibility labels.
How to use it
Install the native package
The package requires the React Native New Architecture and the react-native-nitro-modules peer dependency. Use an iOS development build or a bare React Native app with the New Architecture enabled. On Android, web, and SSR, the fallback is an empty View; guard the tab bar with Platform.OS === "ios" when the native selector is required. Install CocoaPods after adding the package.
npm install react-native-cupertino-tabs react-native-nitro-modules
cd ios && pod installRender controlled tabs
Tabs.Root uses a string value for the selected trigger. Keep the root controlled with value when the screen owns the selection state; onValueChange receives the committed value and its index.
Use defaultValue="library" for an uncontrolled root. A trigger value falls back to its label text and then its index when value is omitted. Set explicit values when labels are repeated or localized.
import { useState } from "react";
import * as Tabs from "react-native-cupertino-tabs";
export default function MediaTabs() {
const [tab, setTab] = useState("library");
return (
<Tabs.Root
value={tab}
onValueChange={(value) => setTab(value)}
>
<Tabs.Trigger value="listen">
<Tabs.Icon name="waveform" />
<Tabs.Label>Listen</Tabs.Label>
</Tabs.Trigger>
<Tabs.Trigger value="library">
<Tabs.Icon name="square.stack.fill" />
<Tabs.Label>Library</Tabs.Label>
</Tabs.Trigger>
<Tabs.Trigger value="search">
<Tabs.Icon name="magnifyingglass" />
<Tabs.Label>Search</Tabs.Label>
</Tabs.Trigger>
</Tabs.Root>
);
}Configure the track and selection pill
Pass tabBarOptions to Tabs.Root for the native track, selected pill, layout, and feedback behavior. backgroundColor: "transparent" hides the native track while keeping the selected pill active. Colors accept hexadecimal values, rgb(), rgba(), and transparent.
<Tabs.Root
defaultValue="home"
tabBarOptions={{
direction: "column",
height: 56,
iconSize: 22,
backgroundColor: "transparent",
selectedBackgroundColor: "#ffffff",
activeTintColor: "#000000",
inactiveTintColor: "#8e8e93",
}}
>
<Tabs.Trigger value="home">
<Tabs.Icon name="house.fill" />
<Tabs.Label>Home</Tabs.Label>
</Tabs.Trigger>
<Tabs.Trigger value="search">
<Tabs.Icon name="magnifyingglass" />
<Tabs.Label>Search</Tabs.Label>
</Tabs.Trigger>
</Tabs.Root>Use custom icons and labels
Tabs.Icon accepts an SF Symbol through name or an image through source. name takes priority when both are set. Source artwork keeps its own colors until the icon receives a color prop. A source icon fills its box and crops overflow like resizeMode: "cover".
<Tabs.Trigger value="profile" accessibilityLabel="Profile">
<Tabs.Icon
source={require("./assets/profile.png")}
width={22}
height={22}
borderRadius={11}
/>
<Tabs.Label style={{ fontSize: 13, fontWeight: "600" }}>
Profile
</Tabs.Label>
</Tabs.Trigger>Component API
Compound components
Tabs.Root and Tabs.Trigger accept string identifiers. onValueChange has the signature (value: string, index: number) => void. A bare text child also works inside Tabs.Trigger when an icon or standalone label is unnecessary.
| Component | Important props | Role |
|---|---|---|
Tabs.Root | value, defaultValue, onValueChange, tabBarOptions, style | Owns selection and renders the native control. |
Tabs.Trigger | value, disabled, accessibilityLabel | Defines one selectable segment. |
Tabs.Icon | name, source, size, color, weight, width, height, borderRadius | Renders an SF Symbol or image icon. |
Tabs.Label | style | Renders a label with per-label typography. |
tabBarOptions
These options control dimensions, colors, layout, spacing, icon size, and haptic feedback for the native control.
| Option | Type | Default |
|---|---|---|
width | number | Stretches to the parent. |
height | number | 44 for row, 56 for column. |
backgroundColor | string | System track. |
selectedBackgroundColor | string | System selection pill. |
cornerRadius | number | Native shape. |
activeTintColor | string | label. |
inactiveTintColor | string | secondaryLabel. |
direction | "row" | "column" | "row". |
gap | number | 5 for row, 3 for column. |
iconSize | number | 17. |
apportionsSegmentWidthsByContent | boolean | false. |
hapticFeedback | boolean | true. |
Interaction and platform notes
Selection commits on release. During a drag, the selection pill follows the finger immediately while icon and label colors stay with the committed tab. Releasing over another segment commits that segment.
In column layout, the control shares one icon row and one label row across segments. A trigger with no label centers its icon in the full height. Label font size does not resize the adjacent icon.
Alternatives and related resources
- React Native Bottom Tabs with Native Components
- Segmented Control For React Native
- React Native Collapsible Header Tabs Component
- React Native Cupertino Tabs on GitHub
FAQs
Q: Does this package render a native control on Android?
A: No. The package wraps iOS UISegmentedControl. Android, web, and SSR render an empty View with the requested size.
Q: Is the React Native New Architecture required?
A: Yes. The package also requires react-native-nitro-modules as a peer dependency.
Q: When does onValueChange fire during a drag?
A: Selection commits when the finger is released. The pill follows the finger before the release, while the committed colors stay on the current tab.
Q: How do I keep a custom icon’s colors?
A: Pass the asset through source and omit the icon’s color prop. Source artwork keeps its own palette until a color is set.
Q: How do I use an uncontrolled tab root?
A: Set defaultValue on Tabs.Root and use onValueChange when the screen needs to react to a committed selection.





