An iOS-style segmented control for React Native β pure JavaScript, one component, zero native code.
- πͺΆ Zero dependencies β plain React Native, works everywhere RN works
- π¬ Springy sliding indicator β animated with the native driver
- π Controlled or uncontrolled β pass
valueto own the state, or let it manage itself - π§© Any element as a tab β strings, icons, or fully composed views
- π¨ Fully themeable β track, indicator, text, per-tab styles
- π³ Inset shadow β recess the track with an inner shadow (new in 2.2)
- π RTL support β indicator direction follows
I18nManager - βΏοΈ Accessible β tabs expose
buttonrole withselected/disabledstates - π New Architecture ready β even tab distribution and correct indicator offsets under Fabric
- π¦ TypeScript β written in TS, types shipped in the box
npm install react-native-segmented-control-2
# or
yarn add react-native-segmented-control-2import SegmentedControl from "react-native-segmented-control-2";
<SegmentedControl
tabs={["Day", "Week", "Month"]}
onChange={(index) => console.log(index)}
/>;That's the whole API: an array of tabs in, the pressed index out.
Pass value to drive the selection from your own state β useful when other
UI (buttons, gestures, remote state) can change the selection too.
const [index, setIndex] = useState(0);
<SegmentedControl
tabs={["One", "Two", "Three"]}
value={index}
onChange={setIndex}
/>;Without value, the component manages its own state; use initialIndex to
pick the starting tab.
Track, indicator, and text colors are all styleable:
<SegmentedControl
tabs={["Income", "Expenses", "Exchange"]}
style={{ backgroundColor: "#D6E9FF" }}
activeTabColor="#0A84FF"
activeTextColor="#FFFFFF"
textStyle={{ color: "#0A84FF" }}
onChange={(index) => console.log(index)}
/>Recess the track with an inner shadow so the raised indicator visually sits
inside it. Pass true for a sensible default, or a CSS-like boxShadow
string for full control:
<SegmentedControl
tabs={["Inbox", "Archive", "Trash"]}
insetShadow
onChange={(index) => console.log(index)}
/>
<SegmentedControl
tabs={["Inbox", "Archive", "Trash"]}
insetShadow="inset 0 3px 8px rgba(0, 0, 0, 0.35)"
onChange={(index) => console.log(index)}
/>Uses React Native's
boxShadowstyle, available on RN 0.76+. On older versions the prop is ignored and the track stays flat.
Tabs don't have to be strings β pass any React element:
<SegmentedControl
tabs={[
<Icon name="book" />,
<Icon name="glasses" />,
<Icon name="history" />,
]}
onChange={(index) => console.log(index)}
/>Compose freely β icon + label rows, badges, whatever you need. See the example app for an icon-and-label "model picker" tab.
tabStyle accepts a plain style or a function of the tab index:
<SegmentedControl
tabs={["S", "M", "L", "XL"]}
tabStyle={(index) => ({ opacity: index === 3 ? 0.5 : 1 })}
onChange={(index) => console.log(index)}
/><SegmentedControl
tabs={["Draft", "Published"]}
disabled={isLocked}
testID="status-switch" // tabs get "status-switch-tab-0", "-tab-1", β¦
onChange={setStatus}
/>| Property | Type | Default | Description |
|---|---|---|---|
tabs |
(string | ReactElement)[] |
required | the tabs to render |
onChange |
(index: number) => void |
required | called with the index of the pressed tab |
value |
number |
undefined | selected index, when used as a controlled component |
| Property | Type | Default | Description |
|---|---|---|---|
initialIndex |
number |
0 |
starting tab for uncontrolled usage |
style |
ViewStyle |
default | style of the outer track container |
activeTabColor |
string |
#FFF |
color of the sliding indicator |
activeTextColor |
string |
#000 |
text color of the active tab |
textStyle |
TextStyle |
default | style of every tab's text |
activeTextStyle |
TextStyle |
default | extra text style applied only to the active tab |
tabStyle |
ViewStyle | (index: number) => ViewStyle |
default | style of each tab; pass a function for per-tab styles |
selectedTabStyle |
ViewStyle |
default | extra style for the sliding indicator |
gap |
number |
2 |
inset between the indicator and the track edges |
insetShadow |
boolean | string |
false |
recess the track with an inner shadow; true uses a default, or pass a boxShadow string to customize (RN 0.76+) |
disabled |
boolean |
false |
stop the tabs from responding to presses |
testID |
string |
undefined | set on the container; each tab also gets `${testID}-tab-${index}` |
Props and tab types are exported:
import SegmentedControl, {
SegmentedControlProps,
TabItem,
} from "react-native-segmented-control-2";The example/ folder is an Expo app showcasing every feature β
it's the app in the demo above, including an in-app light/dark theme switch.
cd example
npm install
npx expo start --ios # or --androidOriginally inspired by react-native-segmented-control/segmented-control and Karthik-B-06/react-native-segmented-control β built as an actively maintained, pure-JavaScript alternative with more customization.
Thanks to @madox2 for controlled-component support and @philo23 for removing the screen-width dependency.
FreakyCoder, kurayogun@gmail.com
React Native Segmented Control 2 is available under the MIT license. See the LICENSE file for more info.

