SegmentedTabs
In-screen tabs (not navigation): segmented or underline Variants, an animated indicator, and only the selected tab's content rendered.
Light
Dark
Your app is ready.
Inter body text, Inter headings.
a0: Vega, neutral, neutral accent, default radius, Inter.Change PresetInstallation
npx nativecn-cli@latest add segmented-tabsAlso installs: theme, selection-group, pressable, text, icon, use-controllable-state, use-motion.
Packages: react-native-reanimated, react-native-worklets, lucide-react-native@^1.51.0, react-native-svg.
Usage
SegmentedTabs demo
Uncontrolled segmented tabs with icons, and controlled underline tabs.
import { Bell, User } from "lucide-react-native";
import { useState } from "react";
import { View } from "react-native";
import {
SegmentedTabs,
SegmentedTabsContent,
SegmentedTabsList,
SegmentedTabsTrigger,
} from "@/components/segmented-tabs";
import { Text } from "@/components/text";
import { createStyles } from "@/theme";
const useStyles = createStyles((t) => ({ root: { gap: t.spacing[8] } }));
export default function SegmentedTabsDemo() {
const styles = useStyles();
const [period, setPeriod] = useState("week");
return (
<View style={styles.root}>
{/* Uncontrolled, segmented (the default Variant), with icons. */}
<SegmentedTabs defaultValue="account">
<SegmentedTabsList>
<SegmentedTabsTrigger value="account" label="Account" icon={User} />
<SegmentedTabsTrigger value="notifications" label="Notifications" icon={Bell} />
</SegmentedTabsList>
<SegmentedTabsContent value="account">
<Text>Your name, email and password.</Text>
</SegmentedTabsContent>
<SegmentedTabsContent value="notifications">
<Text>Choose what we send you.</Text>
</SegmentedTabsContent>
</SegmentedTabs>
{/* Controlled, underline. */}
<SegmentedTabs value={period} onValueChange={setPeriod} variant="underline">
<SegmentedTabsList>
<SegmentedTabsTrigger value="day" label="Day" />
<SegmentedTabsTrigger value="week" label="Week" />
<SegmentedTabsTrigger value="month" label="Month" />
</SegmentedTabsList>
<SegmentedTabsContent value="day">
<Text>Activity today.</Text>
</SegmentedTabsContent>
<SegmentedTabsContent value="week">
<Text>Activity this week.</Text>
</SegmentedTabsContent>
<SegmentedTabsContent value="month">
<Text>Activity this month.</Text>
</SegmentedTabsContent>
</SegmentedTabs>
</View>
);
}SegmentedTabs demo
Uncontrolled segmented tabs with icons, and controlled underline tabs.
import { Bell, User } from "lucide-react-native";
import { useState } from "react";
import { View } from "react-native";
import {
SegmentedTabs,
SegmentedTabsContent,
SegmentedTabsList,
SegmentedTabsTrigger,
} from "@/components/segmented-tabs";
import { Text } from "@/components/text";
import { createStyles } from "@/theme";
const useStyles = createStyles((t) => ({ root: { gap: t.spacing[8] } }));
export default function SegmentedTabsDemo() {
const styles = useStyles();
const [period, setPeriod] = useState("week");
return (
<View style={styles.root}>
{/* Uncontrolled, segmented (the default Variant), with icons. */}
<SegmentedTabs defaultValue="account">
<SegmentedTabsList>
<SegmentedTabsTrigger value="account" label="Account" icon={User} />
<SegmentedTabsTrigger value="notifications" label="Notifications" icon={Bell} />
</SegmentedTabsList>
<SegmentedTabsContent value="account">
<Text>Your name, email and password.</Text>
</SegmentedTabsContent>
<SegmentedTabsContent value="notifications">
<Text>Choose what we send you.</Text>
</SegmentedTabsContent>
</SegmentedTabs>
{/* Controlled, underline. */}
<SegmentedTabs value={period} onValueChange={setPeriod} variant="underline">
<SegmentedTabsList>
<SegmentedTabsTrigger value="day" label="Day" />
<SegmentedTabsTrigger value="week" label="Week" />
<SegmentedTabsTrigger value="month" label="Month" />
</SegmentedTabsList>
<SegmentedTabsContent value="day">
<Text>Activity today.</Text>
</SegmentedTabsContent>
<SegmentedTabsContent value="week">
<Text>Activity this week.</Text>
</SegmentedTabsContent>
<SegmentedTabsContent value="month">
<Text>Activity this month.</Text>
</SegmentedTabsContent>
</SegmentedTabs>
</View>
);
}Props
SegmentedTabs
| Prop | Type and description |
|---|---|
value | string: the selected tab's value (controlled) |
defaultValue | string: the tab selected first (uncontrolled) |
onValueChange | (value: string) => void: called when the user picks a tab |
variant | "segmented" (default) | "underline" |
disabled | boolean: disables every tab (default false) |
style | ViewStyle, layout only, merged last |
SegmentedTabsList
| Prop | Type and description |
|---|---|
children | SegmentedTabsTrigger elements; announced as a tablist |
style | ViewStyle, layout only, merged last |
SegmentedTabsTrigger
| Prop | Type and description |
|---|---|
value | string: identifies the tab (required) |
label | string: the visible text and accessible name (required) |
icon | LucideIcon: shown before the label |
disabled | boolean: disables this tab (default false) |
style | ViewStyle, layout only, merged last |
SegmentedTabsContent
| Prop | Type and description |
|---|---|
value | string: the tab this panel belongs to; rendered only while it is selected |
style | ViewStyle, merged last |
Variants
segmented,underline
Accessibility
- SegmentedTabsList is a tablist; each SegmentedTabsTrigger has role tab with aria-selected.
- SegmentedTabsContent has role tabpanel.
- A trigger's label is its accessible name, and its tap area is at least 48.
- disabled tabs are announced as disabled.
- The sliding indicator is hidden from screen readers and jumps instantly under Reduce Motion.
Notes
For switching views inside one Screen (Account / Notifications, Day / Week / Month). It is not navigation: never use it in place of Expo Router's Tabs; the bottom tab bar is TabNavigation. Put SegmentedTabsTriggers in a SegmentedTabsList, then one SegmentedTabsContent per tab after it; only the selected Content renders. Works controlled (value + onValueChange) or uncontrolled (defaultValue). Tabs give a light haptic tick when chosen. The indicator slides with the fast motion Token. Styles come from the segmented-tabs.list, segmented-tabs.trigger and segmented-tabs.pressed Style Slots.
Source
The Vega Style's code, exactly as add copies it into your app.
{components}/segmented-tabs.tsx
import type { LucideIcon } from "lucide-react-native";
import {
createContext,
use,
useCallback,
useEffect,
useMemo,
useRef,
useState,
type ReactNode,
type Ref,
} from "react";
import {
View,
type GestureResponderEvent,
type LayoutChangeEvent,
type StyleProp,
type ViewProps,
type ViewStyle,
} from "react-native";
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
import { Icon } from "@/components/icon";
import { Pressable, type PressableProps } from "@/components/primitives/pressable";
import { SelectionGroup, useSelectionItem } from "@/components/primitives/selection-group";
import { Text } from "@/components/text";
import { useControllableState } from "@/hooks/use-controllable-state";
import { useMotion } from "@/hooks/use-motion";
import { createStyles } from "@/theme";
/**
* - `segmented`: a filled track with a raised pill under the selected tab (iOS segmented control).
* - `underline`: tabs on a bottom border with a bar under the selected tab.
*/
const variants = {
segmented: { list: "segmentedList", trigger: "segmentedTrigger", indicator: "pill" },
underline: { list: "underlineList", trigger: "underlineTrigger", indicator: "bar" },
} as const;
export type SegmentedTabsVariant = keyof typeof variants;
type SegmentedTabsContextValue = {
value: string | undefined;
setValue: (value: string) => void;
variant: SegmentedTabsVariant;
disabled: boolean;
};
const SegmentedTabsContext = createContext<SegmentedTabsContextValue | null>(null);
function useSegmentedTabs(part: string): SegmentedTabsContextValue {
const context = use(SegmentedTabsContext);
if (!context) throw new Error(`<${part}> must be used inside <SegmentedTabs>.`);
return context;
}
type TriggerLayout = { x: number; width: number };
type ListContextValue = { reportLayout: (value: string, layout: TriggerLayout) => void };
const ListContext = createContext<ListContextValue | null>(null);
const useStyles = createStyles((t) => {
const list = {
height: t.scaleValue(40),
borderRadius: t.radius.lg,
padding: t.scaleValue(3),
};
const trigger = { borderRadius: t.radius.md };
return {
root: { gap: t.spacing[4] },
segmentedList: {
...list,
flexDirection: "row",
alignItems: "stretch",
borderCurve: "continuous",
backgroundColor: t.colors.muted,
},
underlineList: {
height: list.height,
flexDirection: "row",
alignItems: "stretch",
borderBottomWidth: t.borderWidth.default,
borderBottomColor: t.colors.border,
},
trigger: {
flex: 1,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
gap: t.spacing[2],
paddingHorizontal: t.spacing[3],
},
segmentedTrigger: { ...trigger, borderCurve: "continuous" },
underlineTrigger: {},
pressed: { opacity: 0.7 },
disabled: { opacity: t.opacity.disabled },
indicator: { position: "absolute", left: 0 },
pill: {
top: list.padding,
bottom: list.padding,
borderRadius: trigger.borderRadius,
borderCurve: "continuous",
backgroundColor: t.colors.background,
boxShadow: t.elevation.sm,
},
bar: {
bottom: 0,
height: t.scaleValue(2),
backgroundColor: t.colors.foreground,
},
label: { flexShrink: 1 },
};
});
export type SegmentedTabsProps = Omit<ViewProps, "children"> & {
/** The selected tab (controlled). */
value?: string;
/** The tab selected first (uncontrolled). */
defaultValue?: string;
/** Called with the tab's `value` when the user picks it. */
onValueChange?: (value: string) => void;
/** Default `segmented`. */
variant?: SegmentedTabsVariant;
/** Disables every tab. */
disabled?: boolean;
/** Layout only, merged last onto the root. */
style?: StyleProp<ViewStyle>;
ref?: Ref<View>;
children?: ReactNode;
};
/**
* In-screen tabs: a SegmentedTabsList of SegmentedTabsTriggers, then one SegmentedTabsContent
* per tab (only the selected one renders). Not navigation: use TabNavigation for the bottom bar.
* Works controlled (`value` + `onValueChange`) or uncontrolled (`defaultValue`).
*/
export function SegmentedTabs({
value: valueProp,
defaultValue,
onValueChange,
variant = "segmented",
disabled = false,
style,
children,
...props
}: SegmentedTabsProps) {
const styles = useStyles();
const [value, setValue] = useControllableState<string | undefined>({
value: valueProp,
defaultValue,
onChange: onValueChange as ((next: string | undefined) => void) | undefined,
});
const context = useMemo<SegmentedTabsContextValue>(
() => ({ value, setValue, variant, disabled }),
[value, setValue, variant, disabled],
);
return (
<SegmentedTabsContext value={context}>
<View style={[styles.root, style]} {...props}>
{children}
</View>
</SegmentedTabsContext>
);
}
export type SegmentedTabsListProps = Omit<ViewProps, "children" | "role"> & {
/** Layout only, merged last onto the list. */
style?: StyleProp<ViewStyle>;
ref?: Ref<View>;
children?: ReactNode;
};
/** The row of tabs (`tablist`), with the selected-tab indicator that slides between them. */
export function SegmentedTabsList({ style, children, ...props }: SegmentedTabsListProps) {
const { value, setValue, variant, disabled } = useSegmentedTabs("SegmentedTabsList");
const styles = useStyles();
const motion = useMotion();
const v = variants[variant];
const [layouts, setLayouts] = useState<Record<string, TriggerLayout>>({});
const reportLayout = useCallback((key: string, layout: TriggerLayout) => {
setLayouts((prev) => {
const current = prev[key];
if (current && current.x === layout.x && current.width === layout.width) return prev;
return { ...prev, [key]: layout };
});
}, []);
const listContext = useMemo(() => ({ reportLayout }), [reportLayout]);
// The indicator jumps into place the first time, then slides (instantly under Reduce Motion).
const x = useSharedValue(0);
const width = useSharedValue(0);
const visible = useSharedValue(0);
const placed = useRef(false);
const target = value === undefined ? undefined : layouts[value];
useEffect(() => {
if (!target) {
visible.value = 0;
placed.current = false;
return;
}
if (placed.current) {
const timing = motion.timing("fast");
x.value = withTiming(target.x, timing);
width.value = withTiming(target.width, timing);
} else {
x.value = target.x;
width.value = target.width;
placed.current = true;
}
visible.value = 1;
}, [target, motion, x, width, visible]);
const indicatorStyle = useAnimatedStyle(() => ({
width: width.value,
opacity: visible.value,
transform: [{ translateX: x.value }],
}));
return (
<ListContext value={listContext}>
<SelectionGroup
type="single"
itemRole="tab"
value={value ?? ""}
onValueChange={setValue}
disabled={disabled}
style={[styles[v.list], style]}
{...props}
>
<Animated.View
testID="segmented-tabs-indicator"
pointerEvents="none"
aria-hidden
importantForAccessibility="no-hide-descendants"
style={[styles.indicator, styles[v.indicator], indicatorStyle]}
/>
{children}
</SelectionGroup>
</ListContext>
);
}
export type SegmentedTabsTriggerProps = Omit<
PressableProps,
"children" | "style" | "pressedStyle" | "role" | "haptic" | "loading"
> & {
/** Identifies the tab; the matching SegmentedTabsContent shows while it is selected. */
value: string;
/** The visible text, which is also the accessible name. */
label: string;
/** A Lucide icon component shown before the label. */
icon?: LucideIcon;
/** Layout only, merged last onto the tab. */
style?: StyleProp<ViewStyle>;
};
/** One tab: `role="tab"` with `aria-selected`, a light haptic tick when chosen. */
export function SegmentedTabsTrigger({
value,
label,
icon,
disabled: ownDisabled = false,
onPress,
onLayout,
style,
...props
}: SegmentedTabsTriggerProps) {
const { variant } = useSegmentedTabs("SegmentedTabsTrigger");
const list = use(ListContext);
if (!list) throw new Error("<SegmentedTabsTrigger> must be used inside <SegmentedTabsList>.");
const styles = useStyles();
const { selected, disabled, itemProps } = useSelectionItem({ value, disabled: ownDisabled });
const color = selected ? "foreground" : "mutedForeground";
const { reportLayout } = list;
const handleLayout = useCallback(
(event: LayoutChangeEvent) => {
onLayout?.(event);
const { x, width } = event.nativeEvent.layout;
reportLayout(value, { x, width });
},
[onLayout, reportLayout, value],
);
const handlePress = (event: GestureResponderEvent) => {
itemProps.onPress();
onPress?.(event);
};
return (
<Pressable
{...props}
{...itemProps}
onPress={handlePress}
onLayout={handleLayout}
haptic="selection"
pressedStyle={styles.pressed}
style={[
styles.trigger,
styles[variants[variant].trigger],
disabled ? styles.disabled : null,
style,
]}
>
{icon ? <Icon icon={icon} size="sm" color={color} /> : null}
<Text variant="label" color={color} numberOfLines={1} style={styles.label}>
{label}
</Text>
</Pressable>
);
}
export type SegmentedTabsContentProps = Omit<ViewProps, "role"> & {
/** Shown only while the tab with this `value` is selected. */
value: string;
/** Layout only, merged last. */
style?: StyleProp<ViewStyle>;
ref?: Ref<View>;
};
/** The panel for one tab (`tabpanel`). Only the selected tab's Content is rendered. */
export function SegmentedTabsContent({ value, style, ...props }: SegmentedTabsContentProps) {
const { value: selected } = useSegmentedTabs("SegmentedTabsContent");
if (selected !== value) return null;
return <View role="tabpanel" style={style} {...props} />;
}{components}/segmented-tabs.tsx
import type { LucideIcon } from "lucide-react-native";
import {
createContext,
use,
useCallback,
useEffect,
useMemo,
useRef,
useState,
type ReactNode,
type Ref,
} from "react";
import {
View,
type GestureResponderEvent,
type LayoutChangeEvent,
type StyleProp,
type ViewProps,
type ViewStyle,
} from "react-native";
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
import { Icon } from "@/components/icon";
import { Pressable, type PressableProps } from "@/components/primitives/pressable";
import { SelectionGroup, useSelectionItem } from "@/components/primitives/selection-group";
import { Text } from "@/components/text";
import { useControllableState } from "@/hooks/use-controllable-state";
import { useMotion } from "@/hooks/use-motion";
import { createStyles } from "@/theme";
/**
* - `segmented`: a filled track with a raised pill under the selected tab (iOS segmented control).
* - `underline`: tabs on a bottom border with a bar under the selected tab.
*/
const variants = {
segmented: { list: "segmentedList", trigger: "segmentedTrigger", indicator: "pill" },
underline: { list: "underlineList", trigger: "underlineTrigger", indicator: "bar" },
} as const;
export type SegmentedTabsVariant = keyof typeof variants;
type SegmentedTabsContextValue = {
value: string | undefined;
setValue: (value: string) => void;
variant: SegmentedTabsVariant;
disabled: boolean;
};
const SegmentedTabsContext = createContext<SegmentedTabsContextValue | null>(null);
function useSegmentedTabs(part: string): SegmentedTabsContextValue {
const context = use(SegmentedTabsContext);
if (!context) throw new Error(`<${part}> must be used inside <SegmentedTabs>.`);
return context;
}
type TriggerLayout = { x: number; width: number };
type ListContextValue = { reportLayout: (value: string, layout: TriggerLayout) => void };
const ListContext = createContext<ListContextValue | null>(null);
const useStyles = createStyles((t) => {
const list = {
height: t.scaleValue(32),
borderRadius: t.radius.md,
padding: t.scaleValue(2),
};
const trigger = { borderRadius: t.radius.sm };
return {
root: { gap: t.spacing[4] },
segmentedList: {
...list,
flexDirection: "row",
alignItems: "stretch",
borderCurve: "continuous",
backgroundColor: t.colors.muted,
},
underlineList: {
height: list.height,
flexDirection: "row",
alignItems: "stretch",
borderBottomWidth: t.borderWidth.default,
borderBottomColor: t.colors.border,
},
trigger: {
flex: 1,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
gap: t.spacing[2],
paddingHorizontal: t.spacing[3],
},
segmentedTrigger: { ...trigger, borderCurve: "continuous" },
underlineTrigger: {},
pressed: { opacity: 0.7, transform: [{ scale: 0.98 }] },
disabled: { opacity: t.opacity.disabled },
indicator: { position: "absolute", left: 0 },
pill: {
top: list.padding,
bottom: list.padding,
borderRadius: trigger.borderRadius,
borderCurve: "continuous",
backgroundColor: t.colors.background,
boxShadow: t.elevation.sm,
},
bar: {
bottom: 0,
height: t.scaleValue(2),
backgroundColor: t.colors.foreground,
},
label: { flexShrink: 1 },
};
});
export type SegmentedTabsProps = Omit<ViewProps, "children"> & {
/** The selected tab (controlled). */
value?: string;
/** The tab selected first (uncontrolled). */
defaultValue?: string;
/** Called with the tab's `value` when the user picks it. */
onValueChange?: (value: string) => void;
/** Default `segmented`. */
variant?: SegmentedTabsVariant;
/** Disables every tab. */
disabled?: boolean;
/** Layout only, merged last onto the root. */
style?: StyleProp<ViewStyle>;
ref?: Ref<View>;
children?: ReactNode;
};
/**
* In-screen tabs: a SegmentedTabsList of SegmentedTabsTriggers, then one SegmentedTabsContent
* per tab (only the selected one renders). Not navigation: use TabNavigation for the bottom bar.
* Works controlled (`value` + `onValueChange`) or uncontrolled (`defaultValue`).
*/
export function SegmentedTabs({
value: valueProp,
defaultValue,
onValueChange,
variant = "segmented",
disabled = false,
style,
children,
...props
}: SegmentedTabsProps) {
const styles = useStyles();
const [value, setValue] = useControllableState<string | undefined>({
value: valueProp,
defaultValue,
onChange: onValueChange as ((next: string | undefined) => void) | undefined,
});
const context = useMemo<SegmentedTabsContextValue>(
() => ({ value, setValue, variant, disabled }),
[value, setValue, variant, disabled],
);
return (
<SegmentedTabsContext value={context}>
<View style={[styles.root, style]} {...props}>
{children}
</View>
</SegmentedTabsContext>
);
}
export type SegmentedTabsListProps = Omit<ViewProps, "children" | "role"> & {
/** Layout only, merged last onto the list. */
style?: StyleProp<ViewStyle>;
ref?: Ref<View>;
children?: ReactNode;
};
/** The row of tabs (`tablist`), with the selected-tab indicator that slides between them. */
export function SegmentedTabsList({ style, children, ...props }: SegmentedTabsListProps) {
const { value, setValue, variant, disabled } = useSegmentedTabs("SegmentedTabsList");
const styles = useStyles();
const motion = useMotion();
const v = variants[variant];
const [layouts, setLayouts] = useState<Record<string, TriggerLayout>>({});
const reportLayout = useCallback((key: string, layout: TriggerLayout) => {
setLayouts((prev) => {
const current = prev[key];
if (current && current.x === layout.x && current.width === layout.width) return prev;
return { ...prev, [key]: layout };
});
}, []);
const listContext = useMemo(() => ({ reportLayout }), [reportLayout]);
// The indicator jumps into place the first time, then slides (instantly under Reduce Motion).
const x = useSharedValue(0);
const width = useSharedValue(0);
const visible = useSharedValue(0);
const placed = useRef(false);
const target = value === undefined ? undefined : layouts[value];
useEffect(() => {
if (!target) {
visible.value = 0;
placed.current = false;
return;
}
if (placed.current) {
const timing = motion.timing("fast");
x.value = withTiming(target.x, timing);
width.value = withTiming(target.width, timing);
} else {
x.value = target.x;
width.value = target.width;
placed.current = true;
}
visible.value = 1;
}, [target, motion, x, width, visible]);
const indicatorStyle = useAnimatedStyle(() => ({
width: width.value,
opacity: visible.value,
transform: [{ translateX: x.value }],
}));
return (
<ListContext value={listContext}>
<SelectionGroup
type="single"
itemRole="tab"
value={value ?? ""}
onValueChange={setValue}
disabled={disabled}
style={[styles[v.list], style]}
{...props}
>
<Animated.View
testID="segmented-tabs-indicator"
pointerEvents="none"
aria-hidden
importantForAccessibility="no-hide-descendants"
style={[styles.indicator, styles[v.indicator], indicatorStyle]}
/>
{children}
</SelectionGroup>
</ListContext>
);
}
export type SegmentedTabsTriggerProps = Omit<
PressableProps,
"children" | "style" | "pressedStyle" | "role" | "haptic" | "loading"
> & {
/** Identifies the tab; the matching SegmentedTabsContent shows while it is selected. */
value: string;
/** The visible text, which is also the accessible name. */
label: string;
/** A Lucide icon component shown before the label. */
icon?: LucideIcon;
/** Layout only, merged last onto the tab. */
style?: StyleProp<ViewStyle>;
};
/** One tab: `role="tab"` with `aria-selected`, a light haptic tick when chosen. */
export function SegmentedTabsTrigger({
value,
label,
icon,
disabled: ownDisabled = false,
onPress,
onLayout,
style,
...props
}: SegmentedTabsTriggerProps) {
const { variant } = useSegmentedTabs("SegmentedTabsTrigger");
const list = use(ListContext);
if (!list) throw new Error("<SegmentedTabsTrigger> must be used inside <SegmentedTabsList>.");
const styles = useStyles();
const { selected, disabled, itemProps } = useSelectionItem({ value, disabled: ownDisabled });
const color = selected ? "foreground" : "mutedForeground";
const { reportLayout } = list;
const handleLayout = useCallback(
(event: LayoutChangeEvent) => {
onLayout?.(event);
const { x, width } = event.nativeEvent.layout;
reportLayout(value, { x, width });
},
[onLayout, reportLayout, value],
);
const handlePress = (event: GestureResponderEvent) => {
itemProps.onPress();
onPress?.(event);
};
return (
<Pressable
{...props}
{...itemProps}
onPress={handlePress}
onLayout={handleLayout}
haptic="selection"
pressedStyle={styles.pressed}
style={[
styles.trigger,
styles[variants[variant].trigger],
disabled ? styles.disabled : null,
style,
]}
>
{icon ? <Icon icon={icon} size="sm" color={color} /> : null}
<Text variant="label" color={color} numberOfLines={1} style={styles.label}>
{label}
</Text>
</Pressable>
);
}
export type SegmentedTabsContentProps = Omit<ViewProps, "role"> & {
/** Shown only while the tab with this `value` is selected. */
value: string;
/** Layout only, merged last. */
style?: StyleProp<ViewStyle>;
ref?: Ref<View>;
};
/** The panel for one tab (`tabpanel`). Only the selected tab's Content is rendered. */
export function SegmentedTabsContent({ value, style, ...props }: SegmentedTabsContentProps) {
const { value: selected } = useSegmentedTabs("SegmentedTabsContent");
if (selected !== value) return null;
return <View role="tabpanel" style={style} {...props} />;
}
