SegmentTabs
A segmented tab strip: a muted track with a raised white pill on the active segment.
Web-only — segment-tabs.web.tsx has no native counterpart.
Segments size to their content rather than stretching to equal widths, so a
long label like "Submit for review" never wraps. Arrow keys, Home and End
move between segments; the strip is a real tablist.
function Controlled({ options, initial }: { options: readonly { id: string; label: string; badge?: number }[]; initial: string }) {
const [value, setValue] = React.useState(initial);
return <SegmentTabs options={options} value={value} onChange={setValue} />;
}
<Controlled options={BASIC} initial="profile" />Import
import { SegmentTabs } from '@simal/ui/nativewindui';Props
| Prop | Values | Default | Description |
|---|---|---|---|
options | — | — | Ordered segments. Each needs an id and label; badge adds a count pill, and is hidden when zero. |
value | — | "profile" | The selected segment id. Controlled — the strip renders no selection of its own. |
onChange | — | — | Required. Receives the newly selected id. |
Examples
With Badges
A badge carries a count. Zero is not rendered, so an empty tab looks clean rather than showing a 0.
function Controlled({ options, initial }: { options: readonly { id: string; label: string; badge?: number }[]; initial: string }) {
const [value, setValue] = React.useState(initial);
return <SegmentTabs options={options} value={value} onChange={setValue} />;
}
<Controlled
options={[
{ id: 'all', label: 'All', badge: 42 },
{ id: 'open', label: 'Open', badge: 12 },
{ id: 'closed', label: 'Closed', badge: 0 },
]}
initial="all"
/>Mixed Label Lengths
Segments size to their content, so mixed-length labels do not stretch to a common width.
function Controlled({ options, initial }: { options: readonly { id: string; label: string; badge?: number }[]; initial: string }) {
const [value, setValue] = React.useState(initial);
return <SegmentTabs options={options} value={value} onChange={setValue} />;
}
<Controlled
options={[
{ id: 'draft', label: 'Draft' },
{ id: 'review', label: 'Submit for review' },
{ id: 'done', label: 'Done' },
]}
initial="review"
/>Two Segments
Two segments is the minimum useful strip, and reads as a binary switch.
function Controlled({ options, initial }: { options: readonly { id: string; label: string; badge?: number }[]; initial: string }) {
const [value, setValue] = React.useState(initial);
return <SegmentTabs options={options} value={value} onChange={setValue} />;
}
<View className="gap-6">
<Controlled options={[{ id: 'list', label: 'List' }, { id: 'board', label: 'Board' }]} initial="list" />
<Controlled options={BASIC} initial="members" />
</View>InlineField
The dense-grid sibling of Field: same 48px height and label treatment, but it supports a read-only mode and a monospace value, so it doubles as a display row.
SelectField
A dropdown that matches Field and InlineField in height and label treatment, so the three interleave in a form grid without any of them riding high.