BetaNavigation

Segment

A filter: one of a few options, chosen in place.

Overview

A filter: one of a few options, chosen in place.

First example

<Segment value={view} onValueChange={setView}>
<Segment.Item value="dashboard">Dashboard</Segment.Item>
<Segment.Item value="analytics">Analytics</Segment.Item>
</Segment>

Anatomy

slotwhat it is
SegmentThe track, and the pill that slides across it
Segment.ItemOne option
Segment.LabelThe word on it — written for you from a text child

Usage

<Segment value={view} onValueChange={setView}>
  <Segment.Item value="dashboard">Dashboard</Segment.Item>
  <Segment.Item value="analytics">Analytics</Segment.Item>
</Segment>

Root props

SegmentProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
childrenReactNodeNo
sizeSegmentSize | undefinedNo
radiusRadiusKey | undefinedNo
colorstring | undefinedNoThe tint (R7) — a raw value, never a token.
valuestring | undefinedNo
defaultValuestring | undefinedNo
onValueChange((value: string) => void) | undefinedNo
hasSeparatorboolean | undefinedNoWhether a hairline is drawn between the options the pill is nowhere near. Off by default: the pill already says which option is chosen, and the rules are what a segment adds when its options are a long enough list to need dividing. Both edges of the pill stay clear — see `hasLeadingSeparator`.
isDisabledboolean | undefinedNo
asChildboolean | undefinedNo

Slots

SegmentItemProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
valuestringYes
childrenReactNode | ((state: SegmentItemRenderState) => ReactNode)No
isDisabledboolean | undefinedNo
asChildboolean | undefinedNo

SegmentLabelProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
childrenReactNodeNo

Variants, sizes and colour

This component adds no visual axis of its own. The values it does take are in the generated types above and in the live demo.

Accessibility

The source defines no extra rule. Keep the React Native labels, roles, states and focus order your case needs, then check the result with VoiceOver and TalkBack.

Migration from legacy

This component declares no rule of its own. The migration guide covers variants, colours and slots.

Implementation notes

It is not `Tabs`

They wear the same clothes: a pill sliding under the chosen option inside a filled track, on the theme's own segment / segmentForeground tokens. They do different jobs.

TabsSegment
what it is forplaces to goa value something else reads
what it holdspanels, through Tabs.Contentnothing — the value is yours
what it announcestablist / tabradiogroup / radio
the indicatora slot; light has nonedrawn by the root, always

A tab bar wraps content: its triggers name panels that live under it. A segment names nothing — it holds a value, the way a radio group does, and what reads that value is somewhere else on the screen. Which of the two a control is, is what a screen reader hears, so it cannot be a flag on one component.

The pill is not a slot

Tabs makes you write <Tabs.Indicator /> because a tab bar can be light and have no indicator at all. A segment without its pill is not a segment, so the root draws it and there is nothing to remember.

color is how you move it — see below.

Separators, for a list long enough to need dividing

hasSeparator draws a hairline between the options the pill is nowhere near. It is off by default: the pill already says which option is chosen.

Both edges of the pill stay clear. A rule running into a raised surface reads as a crack in it, so neither the chosen option's own leading rule nor the one belonging to the option after it is drawn. That is the behaviour iOS has had since the segmented control was introduced, and it is why one does not look like a table.

The rule belongs to the option on its trailing side, which is what lets an option decide alone: the root has no way to know which of its children is which without reading their props, and reading a child's props is introspection this library does not do. Every option already publishes its rectangle for the pill to slide to, and an ordering is all hasLeadingSeparator needs — so before the first layout nothing is drawn, and the rules arrive with the pill rather than a frame ahead of it.

It hugs its options

A segment as wide as the screen with two options in it is a navigation bar pretending to be a filter. A row that has to fill its parent is a style away, as everywhere else in this library — there is no fullWidth prop.

The tint reaches the word, not just the pill

color moves the pill and the label on it. fgSelected is a role rather than a token named in a state, which is what makes the tint follow into the chosen option's colour — the tint pass re-runs paint, not the axes. Without that, a tinted segment would slide a coloured pill under a word that had stopped reading against it.

The pill slides on the UI thread

useSlidingIndicator, shared with the Tabs. Nothing is drawn before the first layout — an indicator at zero width would flash at the start of the row — and the first placement jumps where every one after it springs, because animating the first reads as the control arranging itself rather than as one at rest.

Everything else is a style prop

padding, gap, backgroundColor — full RN names, full RN values (R14), on the root and on every slot.