suiss/uim · React Native Component

Tabs

A set of tabs that switch between panels of content shown in the same place.

Tabs is an open-source mobile component from suiss/uim for React Native and Expo, with SwiftUI and Jetpack Compose versions built from the same design tokens. The @esuiss/uim CLI copies its source into your project, so you own the code.

iOS
Tabs on iOS in light mode

Tabs groups a TabsList of TabsTrigger buttons with matching TabsContent panels. The active tab is marked by an indicator that slides between tabs with a Reanimated layout transition.

TabsList has two variants: default, a segmented control with a raised indicator on a filled track, and line, a transparent list with a thin underline. Both work in horizontal and vertical orientation.

Installation

$ pnpm dlx @esuiss/uim@latest add tabs

The CLI also adds what it builds on: Text.

Manual

  1. Install the dependencies:
npx expo install class-variance-authority cn react-native-reanimated react-native-worklets
  1. Add the pieces it builds on: Text, and the primitives components/primitives/tabs.tsx, components/primitives/use-render.tsx.

  2. Copy the source into components/ui/tabs.tsx:

uniwind/components/ui/tabs.tsx
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "cn"
import * as React from "react"
import Animated, {
  Easing,
  LinearTransition,
  ReduceMotion,
} from "react-native-reanimated"
import * as TabsPrimitive from "@/components/primitives/tabs"
import { resolveClassName } from "@/components/primitives/use-render"
import { TextClassContext } from "@/components/ui/text"

type TabsListVariant = NonNullable<
  VariantProps<typeof tabsListVariants>["variant"]
>
  1. Update the import paths to match your project.

Usage

import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
<TabsDemo />

Give each TabsTrigger and its TabsContent the same value. Use defaultValue for an uncontrolled tab set, or value with onValueChange to control it.

Set orientation="vertical" on Tabs to stack the list beside the panels. Put an Icon next to the Text in a trigger for icon tabs.

Inactive panels are unmounted unless you set keepMounted on TabsContent. tabsListVariants is exported for reuse.

Examples

With icons

Tabs with an icon beside each label.

iOS
Tabs (with icons) on iOS in light mode

Disabled tab

A tab set with one disabled tab.

iOS
Tabs (disabled tab) on iOS in light mode

Line

The line variant with an underline indicator.

iOS
Tabs (line) on iOS in light mode

Vertical

A vertical tab list next to its panels.

iOS
Tabs (vertical) on iOS in light mode

Vertical line

A vertical line list with a side indicator.

iOS
Tabs (vertical line) on iOS in light mode

With icons line

Icon tabs in the line variant.

iOS
Tabs (with icons line) on iOS in light mode

Disabled tab line

A disabled tab in the line variant.

iOS
Tabs (disabled tab line) on iOS in light mode

API Reference

Tabs

Root that holds the active value and orientation.

ExtendsReact.ComponentProps<typeof TabsPrimitive.Root>

TabsList

Row or column of triggers with the animated indicator.

ExtendsReact.ComponentProps<typeof TabsPrimitive.List>
PropType
About
default for a filled segmented track, or line for an underline.
Type
"default""line"
Default
"default"

TabsTrigger

Pressable tab that activates its panel.

ExtendsReact.ComponentProps<typeof TabsPrimitive.Tab>

TabsContent

Panel shown when its value is active.

ExtendsReact.ComponentProps<typeof TabsPrimitive.Panel>

Accessibility

  • TabsList has the tablist role and each trigger has the tab role.
  • Triggers report selected and disabled in their accessibility state, so VoiceOver and TalkBack announce the current tab.
  • Hidden panels kept with keepMounted are removed from the accessibility tree.
  • The indicator animation follows the system reduce motion setting.

Notes

  • The default track uses bg-control with a bg-surface-raised indicator (bg-label-quaternary in dark mode); the line indicator uses bg-label.
  • Labels use text-label when active, text-label-secondary when inactive and text-label-quaternary when disabled.
  • Light and dark colors follow the system appearance.