Description:
react-native-segmented-control-2 is a React Native segmented control that creates an iOS-style sliding tab selector on Android and iOS.
It keeps the interaction in one component with a spring indicator, controlled or uncontrolled state, custom tab elements, and RTL movement.
Preview

Features
- Spring-animated sliding selection indicator.
- Controlled and uncontrolled selection state.
- String, icon, and composed React element tabs.
- Track, indicator, text, and per-tab style controls.
- RTL-aware indicator movement.
- Button role with selected and disabled accessibility states.
- TypeScript declarations and Fabric-aware tab distribution.
How To Use It
Install the package
Install the package in the React Native app:
# Add the JavaScript segmented control package.
npm install react-native-segmented-control-2
# Yarn users can run this command instead.
# yarn add react-native-segmented-control-2Render a basic control
Pass the tab labels through tabs and read the selected index from onChange:
valuemakes the control controlled. Removevaluewhen the component should manage the selected index itself. SetinitialIndexfor the starting position in uncontrolled mode.
import { useState } from "react";
import { StyleSheet, Text, View } from "react-native";
import SegmentedControl from "react-native-segmented-control-2";
export function ReportFilter() {
const [selectedIndex, setSelectedIndex] = useState(0);
return (
<View style={styles.screen}>
<SegmentedControl
tabs={["Summary", "Invoices", "Alerts"]}
value={selectedIndex}
onChange={setSelectedIndex}
/>
<Text style={styles.status}>Selected tab: {selectedIndex}</Text>
</View>
);
}
const styles = StyleSheet.create({
screen: { gap: 16, padding: 24 },
status: { fontSize: 16 },
});Use custom tab content
Each entry in tabs accepts a string or a React element. Use composed content when labels need icons, badges, or custom layout:
import { Text, View } from "react-native";
import SegmentedControl from "react-native-segmented-control-2";
const tab = (symbol: string, label: string) => (
<View style={{ alignItems: "center", flexDirection: "row", gap: 6 }}>
<Text>{symbol}</Text>
<Text>{label}</Text>
</View>
);
export function AccountSections() {
return (
<SegmentedControl
tabs={[
tab("$", "Budget"),
tab("#", "Limits"),
tab("@", "Team"),
]}
onChange={(index) => console.log("Section index:", index)}
/>
);
}Customize the track and indicator
Use the color props for the common case. Use style, textStyle, activeTextStyle, tabStyle, and selectedTabStyle when the layout needs more control:
<SegmentedControl
tabs={["Open", "Pending", "Closed"]}
style={{ backgroundColor: "#E8EEF8", borderRadius: 12 }}
activeTabColor="#1D4ED8"
activeTextColor="#FFFFFF"
textStyle={{ color: "#1E3A8A", fontSize: 13 }}
activeTextStyle={{ fontWeight: "700" }}
tabStyle={(index) => ({ opacity: index === 2 ? 0.75 : 1 })}
selectedTabStyle={{ borderRadius: 10 }}
onChange={(index) => console.log("Filter index:", index)}
/>Disable interaction and add test IDs
The disabled prop blocks tab presses. The root receives testID, and each tab receives a derived ID in the form ${testID}-tab-${index}:
<SegmentedControl
tabs={["Edit", "Preview"]}
disabled={isSaving}
testID="document-mode"
onChange={setMode}
/>All Component Props
| Prop | Type | Default | Description |
|---|---|---|---|
tabs | (string | ReactElement)[] | required | Items rendered by the control. |
onChange | (index: number) => void | required | Runs with the index of the pressed tab. |
value | number | undefined | Selected index for controlled usage. |
initialIndex | number | 0 | Starting index for uncontrolled usage. |
style | StyleProp<ViewStyle> | default | Style for the outer track container. |
activeTabColor | string | #FFF | Background color of the sliding indicator. |
activeTextColor | string | #000 | Text color for an active string tab. |
textStyle | StyleProp<TextStyle> | default | Style applied to string tab text. |
activeTextStyle | StyleProp<TextStyle> | default | Extra text style for the active string tab. |
tabStyle | StyleProp<ViewStyle> | (index: number) => StyleProp<ViewStyle> | default | Style for each tab or a function that returns a style by index. |
selectedTabStyle | StyleProp<ViewStyle> | default | Extra style for the sliding indicator. |
gap | number | 2 | Inset between the indicator and track edges. |
insetShadow | boolean | string | false | Adds the default inset shadow or a custom boxShadow string on React Native 0.76+. |
disabled | boolean | false | Blocks tab presses. |
testID | string | undefined | Sets the container test ID and derived tab test IDs. |
Events and Callbacks
<SegmentedControl
tabs={["Newest", "Popular", "Following"]}
onChange={(index) => {
// Store the index or trigger a view-specific update.
console.log("New selection:", index);
}}
/>