React Native Segmented Control 2

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

react-native-segmented-control-2

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-2

Render a basic control

Pass the tab labels through tabs and read the selected index from onChange:

value makes the control controlled. Remove value when the component should manage the selected index itself. Set initialIndex for 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

PropTypeDefaultDescription
tabs(string | ReactElement)[]requiredItems rendered by the control.
onChange(index: number) => voidrequiredRuns with the index of the pressed tab.
valuenumberundefinedSelected index for controlled usage.
initialIndexnumber0Starting index for uncontrolled usage.
styleStyleProp<ViewStyle>defaultStyle for the outer track container.
activeTabColorstring#FFFBackground color of the sliding indicator.
activeTextColorstring#000Text color for an active string tab.
textStyleStyleProp<TextStyle>defaultStyle applied to string tab text.
activeTextStyleStyleProp<TextStyle>defaultExtra text style for the active string tab.
tabStyleStyleProp<ViewStyle> | (index: number) => StyleProp<ViewStyle>defaultStyle for each tab or a function that returns a style by index.
selectedTabStyleStyleProp<ViewStyle>defaultExtra style for the sliding indicator.
gapnumber2Inset between the indicator and track edges.
insetShadowboolean | stringfalseAdds the default inset shadow or a custom boxShadow string on React Native 0.76+.
disabledbooleanfalseBlocks tab presses.
testIDstringundefinedSets 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);
  }}
/>

Alternatives and Related Resources

Add Comment