@app-studio/components
Tabs
A versatile component for organizing and displaying content in separate, selectable sections.
View component sourceImport
ts
import { Tabs } from '@app-studio/components';New to the library? Complete installation and provider setup first.
Interactive examples
Try the component, then open Code to copy the complete example.
import React, { useState } from 'react';
import { View, Text } from 'app-studio';
import { Button } from '@app-studio/components';
import { Tabs } from '@app-studio/components';
export const ControlledTabs = () => {
const [activeTab, setActiveTab] = useState<string | number>('profile');
const tabs = [
{
title: 'Profile',
value: 'profile',
content: (
<View padding={20}>
<Text fontWeight="600" marginBottom={12} fontSize={18}>
Profile Settings
</Text>
<Text color="color-gray-600">
Manage your public profile, bio, and avatar.
</Text>
</View>
),
},
{
title: 'Account',
value: 'account',
content: (
<View padding={20}>
<Text fontWeight="600" marginBottom={12} fontSize={18}>
Account Details
</Text>
<Text color="color-gray-600">
Update your email, password, and security preferences.
</Text>
</View>
),
},
{
title: 'Notifications',
value: 'notifications',
content: (
<View padding={20}>
<Text fontWeight="600" marginBottom={12} fontSize={18}>
Notification Preferences
</Text>
<Text color="color-gray-600">
Configure how and when you want to be notified.
</Text>
</View>
),
},
];
return (
<View width="100%">
<View
marginBottom={20}
padding={16}
backgroundColor="color-blue-50"
borderRadius={8}
border="1px dashed"
borderColor="color-blue-200"
>
<Text marginBottom={12}>
Current Active Value: <Text fontWeight="bold">{activeTab}</Text>
</Text>
<View display="flex" flexDirection="row" gap={10}>
<Button
size="sm"
variant={activeTab === 'profile' ? 'primary' : 'outline'}
onClick={() => setActiveTab('profile')}
>
Select Profile
</Button>
<Button
size="sm"
variant={activeTab === 'account' ? 'primary' : 'outline'}
onClick={() => setActiveTab('account')}
>
Select Account
</Button>
<Button
size="sm"
variant={activeTab === 'notifications' ? 'primary' : 'outline'}
onClick={() => setActiveTab('notifications')}
>
Select Notifications
</Button>
</View>
</View>
<Tabs
tabs={tabs}
value={activeTab}
onTabChange={(tab) => {
console.log('Tab changed:', tab);
if (tab.value) setActiveTab(tab.value);
}}
/>
</View>
);
};Props
Required props are marked below. A dash indicates an unspecified default. Design system configuration can override implementation defaults.
Scroll horizontally to see every column.
| Prop | Type | Default | Description |
|---|---|---|---|
tabs | Tab[] | — | Array of tab objects to be displayed. Each tab needs a unique `value`. (Data-driven approach)Source |
defaultValue | string | number | — | Optional value of the tab to be active initially. Defaults to the first tab if not provided or not found.Source |
onTabChange | ((activeTab: Tab) => void) | — | Optional callback function triggered when the active tab changes. Receives the newly active tab object.Source |
views | TabsStyles | — | Optional styles object to customize the appearance of the tabs container, headers, text, and content area.Source |
variant | TabsVariant"pill""underline""segmented" | — | Visual presentation style for the tab list.Source |
iconPosition | "bottom" | "left" | "right" | "top""bottom""left""right""top" | — | Optional position of the icon relative to the text in tab headers.Source |
renderTab | ((tab: Tab, isActive: boolean, onClick: () => void) => React.ReactNode) | — | Optional custom renderer for a single tab header. Receives the tab data, whether it's currently active, and an onClick handler to activate the tab.Source |
renderContent | ((activeTab: Tab) => React.ReactNode) | — | Optional custom renderer for the content area. Receives the currently active tab object. If not provided, the `content` property from the active tab object will be rendered.Source |
keepMounted | boolean | — | By default only the ACTIVE tab's content is mounted — inactive tabs do not exist in the DOM (their state resets on switch, and tests must activate a tab before asserting on its content). Set `keepMounted` to mount every tab's content up front and toggle visibility with `display: none`, preserving internal state across switches. Only applies to the data-driven `tabs` path; `renderContent` still receives the active tab only.Source |
value | string | number | — | Current value for controlled compound component patternSource |
onValueChange | ((value: string | number) => void) | — | Callback when tab value changes in compound component pattern. Receives the tab's value — matching `Tabs.type.ts` and `Tabs.state.ts`, which already declared `string | number` while this one said `any`.Source |
children | View typestring | number | bigint | boolean | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | React.ReactPortal | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | null | — | Children for compound component patternSource |
Tabs.List
Scroll horizontally to see every column.
| Prop | Type | Default | Description |
|---|---|---|---|
children Required | View typestring | number | bigint | boolean | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | React.ReactPortal | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | null | — | Children (TabsTrigger components)Source |
views | { container?: ViewProps; } | — | Custom styles for the list containerSource |
Tabs.Trigger
Scroll horizontally to see every column.
| Prop | Type | Default | Description |
|---|---|---|---|
value Required | string | — | Unique value for this tabSource |
children Required | View typestring | number | bigint | boolean | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | React.ReactPortal | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | null | — | Children content for the triggerSource |
disabled | boolean | — | Whether this trigger is disabledSource |
views | { trigger?: ViewProps; activeState?: ViewProps; } | — | Custom styles for the triggerSource |
Tabs.Content
Scroll horizontally to see every column.
| Prop | Type | Default | Description |
|---|---|---|---|
value Required | string | — | Value that this content corresponds toSource |
children Required | View typestring | number | bigint | boolean | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | React.ReactPortal | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | null | — | Children content to display when this tab is activeSource |
views | { content?: ViewProps; } | — | Custom styles for the contentSource |
For shared layout, color, and responsive properties, read Styling & composition and Responsive layouts. Additional native element attributes depend on the target platform.
Usage guidance
- Give controls a visible label or an accessible name, and verify keyboard interaction. See Accessibility.
- For controlled inputs, update the value from the callback shown in the props table. Keep application state in your component or form library.
- These previews run in the browser. Check React Native setup for platform requirements and differences.