Radix homepageRadix Homepage
PrimitivesAlpha

Tabs

A set of layered sections of content—known as tab panels—that are displayed one at a time.

import React from 'react';
import { styled } from '@stitches/react';
import { violet, mauve, blackA, green } from '@radix-ui/colors';
import * as TabsPrimitive from '@radix-ui/react-tabs';
const StyledTabs = styled(TabsPrimitive.Root, {
display: 'flex',
flexDirection: 'column',
width: 300,
boxShadow: `0 2px 10px ${blackA.blackA4}`,
});
const StyledList = styled(TabsPrimitive.List, {
flexShrink: 0,
display: 'flex',
borderBottom: `1px solid ${mauve.mauve6}`,
});
const StyledTrigger = styled(TabsPrimitive.Trigger, {
all: 'unset',
fontFamily: 'inherit',
backgroundColor: 'white',
padding: '0 20px',
height: 45,
flex: 1,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 15,
lineHeight: 1,
color: mauve.mauve11,
userSelect: 'none',
'&:first-child': { borderTopLeftRadius: 6 },
'&:last-child': { borderTopRightRadius: 6 },
'&:hover': { color: violet.violet11 },
'&[data-state="active"]': {
color: violet.violet11,
boxShadow: 'inset 0 -1px 0 0 currentColor, 0 1px 0 0 currentColor',
},
'&:focus': { position: 'relative', boxShadow: `0 0 0 2px black` },
});
const StyledContent = styled(TabsPrimitive.Content, {
flexGrow: 1,
padding: 20,
backgroundColor: 'white',
borderBottomLeftRadius: 6,
borderBottomRightRadius: 6,
outline: 'none',
'&:focus': { boxShadow: `0 0 0 2px black` },
});
// Exports
export const Tabs = StyledTabs;
export const TabsList = StyledList;
export const TabsTrigger = StyledTrigger;
export const TabsContent = StyledContent;
// Your app...
const Box = styled('div', {});
const Flex = styled('div', { display: 'flex' });
const Text = styled('div', {
marginBottom: 20,
color: mauve.mauve11,
fontSize: 15,
lineHeight: 1.5,
});
const Button = styled('button', {
all: 'unset',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 4,
padding: '0 15px',
fontSize: 15,
lineHeight: 1,
fontWeight: 500,
height: 35,
variants: {
variant: {
violet: {
backgroundColor: 'white',
color: violet.violet11,
boxShadow: `0 2px 10px ${blackA.blackA7}`,
'&:hover': { backgroundColor: mauve.mauve3 },
'&:focus': { boxShadow: `0 0 0 2px black` },
},
green: {
backgroundColor: green.green4,
color: green.green11,
'&:hover': { backgroundColor: green.green5 },
'&:focus': { boxShadow: `0 0 0 2px ${green.green7}` },
},
},
},
defaultVariants: {
variant: 'violet',
},
});
const Fieldset = styled('fieldset', {
all: 'unset',
marginBottom: 15,
width: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
});
const Label = styled('label', {
fontSize: 13,
lineHeight: 1,
marginBottom: 10,
color: violet.violet12,
display: 'block',
});
const Input = styled('input', {
all: 'unset',
flex: '1 0 auto',
borderRadius: 4,
padding: '0 10px',
fontSize: 15,
lineHeight: 1,
color: violet.violet11,
boxShadow: `0 0 0 1px ${violet.violet7}`,
height: 35,
'&:focus': { boxShadow: `0 0 0 2px ${violet.violet8}` },
});
const TabsDemo = () => (
<Box css={{}}>
<Tabs defaultValue="tab1">
<TabsList aria-label="Manage your account">
<TabsTrigger value="tab1">Account</TabsTrigger>
<TabsTrigger value="tab2">Password</TabsTrigger>
</TabsList>
<TabsContent value="tab1">
<Text>Make changes to your account here. Click save when you're done.</Text>
<Fieldset>
<Label htmlFor="name">Name</Label>
<Input id="name" defaultValue="Pedro Duarte" />
</Fieldset>
<Fieldset>
<Label htmlFor="username">Username</Label>
<Input id="username" defaultValue="@peduarte" />
</Fieldset>
<Flex css={{ marginTop: 20, justifyContent: 'flex-end' }}>
<Button variant="green">Save changes</Button>
</Flex>
</TabsContent>
<TabsContent value="tab2">
<Text>Change your password here. After saving, you'll be logged out.</Text>
<Fieldset>
<Label htmlFor="currentPassword">Current password</Label>
<Input id="currentPassword" type="password" />
</Fieldset>
<Fieldset>
<Label htmlFor="newPassword">New password</Label>
<Input id="newPassword" type="password" />
</Fieldset>
<Fieldset>
<Label htmlFor="confirmPassword">Confirm password</Label>
<Input id="confirmPassword" type="password" />
</Fieldset>
<Flex css={{ marginTop: 20, justifyContent: 'flex-end' }}>
<Button variant="green">Change password</Button>
</Flex>
</TabsContent>
</Tabs>
</Box>
);
export default TabsDemo;

Features

  • Can be controlled or uncontrolled.
  • Supports horizontal/vertical orientation.
  • Supports automatic/manual activation.
  • Full keyboard navigation.

Install the component from your command line.

npm install @radix-ui/react-tabs

Import all parts and piece them together.

import * as Tabs from '@radix-ui/react-tabs';
export default () => (
<Tabs.Root>
<Tabs.List>
<Tabs.Trigger />
</Tabs.List>
<Tabs.Content />
</Tabs.Root>
);

Contains all the tabs component parts.

PropTypeDefault
asenumdiv
defaultValuestringNo default value
valuestringNo default value
onValueChangefunctionNo default value
orientationenum"horizontal"
direnum"ltr"
activationModeenum"automatic"

Contains the triggers that are aligned along the edge of the active content.

PropTypeDefault
asenumdiv
loopbooleantrue

The button that activates its associated content.

PropTypeDefault
asenumdiv
value*stringNo default value
disabledbooleanfalse

Contains the content associated with each trigger.

PropTypeDefault
asenumdiv
value*stringNo default value

You can create vertical tabs by using the orientation prop.

import * as Tabs from '@radix-ui/react-tabs';
export default () => (
<Tabs.Root defaultValue="tab1" orientation="vertical">
<Tabs.List aria-label="tabs example">
<Tabs.Trigger value="tab1">One</Tabs.Trigger>
<Tabs.Trigger value="tab2">Two</Tabs.Trigger>
<Tabs.Trigger value="tab3">Three</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="tab1">Tab one content</Tabs.Content>
<Tabs.Content value="tab2">Tab two content</Tabs.Content>
<Tabs.Content value="tab3">Tab three content</Tabs.Content>
</Tabs.Root>
);

Adheres to the Tabs WAI-ARIA design pattern.

KeyDescription
TabWhen focus moves onto the tabs, focuses the active trigger. When a trigger is focused, moves focus to the active content.
ArrowDownMoves focus to the next trigger depending on orientation and activates its associated content.
ArrowRightMoves focus to the next trigger depending on orientation and activates its associated content.
ArrowUpMoves focus to the previous trigger depending on orientation and activates its associated content.
ArrowLeftMoves focus to the previous trigger depending on orientation and activates its associated content.
HomeMoves focus to the first trigger and activates its associated content.
EndMoves focus to the last trigger and activates its associated content.