Demo
Usage
Import the Tabs
component and pass an array of items and a defaultValue
.
import { Tabs } from '@aws-amplify/ui-react';
export const DefaultTabUncontrolled = () => (
<Tabs
defaultValue={'Tab 1'}
items={[
{ label: 'Tab 1', value: 'Tab 1', content: 'Tab content #1' },
{ label: 'Tab 2', value: 'Tab 2', content: 'Tab content #2' },
{ label: 'Tab 3', value: 'Tab 3', content: 'Tab content #3' },
]}
/>
);
Controlled Tabs
The Tabs component also works as a controlled component. This allows you to programmatically set the current tab by setting the value
prop. You can also use the onValueChange
handler to know when a Tab would change.
Content of the second tab.
import { useState } from 'react';
import { Tabs, Button } from '@aws-amplify/ui-react';
export const ControlledTabExample = () => {
const [tab, setTab] = useState('1');
return (
<Tabs
value={tab}
onValueChange={(tab) => setTab(tab)}
items={[
{
label: 'First',
value: '1',
content: 'Content of the first tab',
},
{
label: 'Second',
value: '2',
content: (
<>
<p>Content of the second tab.</p>
<Button isFullWidth onClick={() => setTab('1')}>
Go to first tab
</Button>
</>
),
},
]}
/>
);
};
Lazy loading tabs
By default, each tab's content is always rendered into the DOM and hidden with CSS. This allows search engines and users (using cmd/ctrl+F) to see all the tab content. Sometimes this is not preferrable and you would rather only load tab content when a tab is shown. To do that use the isLazy
prop.
import { Tabs } from '@aws-amplify/ui-react';
export const LazyTabsExample = () => {
return (
<Tabs
defaultValue="1"
items={[
{ label: 'Tab 1', value: '1', content: 'Tab 1 content' },
{ label: 'Tab 2', value: '2', content: 'Tab 2 content' },
{ label: 'Tab 3', value: '3', content: 'Tab 3 content' },
]}
isLazy
/>
);
};
Tab spacing
In the Tabs
component, use the spacing
prop to control how Tabs take up the remaining space. Pass equal
to make each tab take up the same amount of space, and relative
to make each tab take up space relative to the size of its title.
import { Flex, Tabs } from '@aws-amplify/ui-react';
export const Spacing = () => (
<Flex direction="column" gap="2rem">
<Tabs.Container defaultValue="1">
<Tabs.List spacing="equal">
<Tabs.Item value="1">First</Tabs.Item>
<Tabs.Item value="2">This is the second tab</Tabs.Item>
<Tabs.Item value="3">Really long title for demonstration</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="1">Content of the first tab</Tabs.Panel>
<Tabs.Panel value="2">Content of the second tab</Tabs.Panel>
<Tabs.Panel value="3">Content of the third tab</Tabs.Panel>
</Tabs.Container>
<Tabs.Container defaultValue="1">
<Tabs.List spacing="relative">
<Tabs.Item value="1">First</Tabs.Item>
<Tabs.Item value="2">This is the second tab</Tabs.Item>
<Tabs.Item value="3">Really long title for demonstration</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="1">Content of the first tab</Tabs.Panel>
<Tabs.Panel value="2">Content of the second tab</Tabs.Panel>
<Tabs.Panel value="3">Content of the third tab</Tabs.Panel>
</Tabs.Container>
</Flex>
);
Justify Content
In the Tabs
component, use the justifyContent
prop to control how space is distributed between and around the Tabs. Available options include flex-start
(default), flex-end
, center
, space-between
, space-around
, and space-evenly
.
Note: this prop only has an effect if the spacing
prop is not set, otherwise the spacing
prop will take precedence.
import { Flex, Tabs } from '@aws-amplify/ui-react';
export const JustifyContent = () => (
<Flex direction="column" gap="2rem">
<Tabs.Container defaultValue="First">
<Tabs.List justifyContent="center">
<Tabs.Item value="First">First</Tabs.Item>
<Tabs.Item value="Second">Second</Tabs.Item>
<Tabs.Item value="Third">Third</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="First">Tabs are centered</Tabs.Panel>
<Tabs.Panel value="Second">Content of the second tab</Tabs.Panel>
<Tabs.Panel value="Third">Content of the third tab</Tabs.Panel>
</Tabs.Container>
<Tabs.Container defaultValue="First">
<Tabs.List justifyContent="flex-end">
<Tabs.Item value="First">First</Tabs.Item>
<Tabs.Item value="Second">Second</Tabs.Item>
<Tabs.Item value="Third">Third</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="First">Tabs are stacked to the right</Tabs.Panel>
<Tabs.Panel value="Second">Content of the second tab</Tabs.Panel>
<Tabs.Panel value="Third">Content of the third tab</Tabs.Panel>
</Tabs.Container>
</Flex>
);
Indicator Position
You can change the position of the tab border and current tab indicator with the indicatorPosition
prop. The only values are top
or bottom
(default).
import { Tabs } from '@aws-amplify/ui-react';
export const IndicatorPosition = () => (
<Tabs.Container>
<Tabs.List indicatorPosition="top">
<Tabs.Item value="1">Tab 1</Tabs.Item>
<Tabs.Item value="2">Tab 2</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="1">Tab 1 content</Tabs.Panel>
<Tabs.Panel value="2">Tab 2 content</Tabs.Panel>
</Tabs.Container>
);
Disabled Tabs
In the TabItem
component, use the isDisabled
prop to make a Tab not clickable and its content not visible to the user.
import { Tabs } from '@aws-amplify/ui-react';
export const DisabledTabs = () => (
<Tabs.Container defaultValue="1">
<Tabs.List>
<Tabs.Item value="1">Tab 1</Tabs.Item>
<Tabs.Item value="2">Tab 2</Tabs.Item>
<Tabs.Item value="3" isDisabled>
Tab 3
</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="1">Tab 1 Content</Tabs.Panel>
<Tabs.Panel value="2">Tab 2 Content</Tabs.Panel>
<Tabs.Panel value="3" isDisabled>
This Tab is not clickable and will not display its content to the user.
</Tabs.Panel>
</Tabs.Container>
);
Composable
The Tabs component is composable so you can rearrange parts of the tabs or access any props for each sub-component. Here are the composable parts of the Tabs component:
<Tabs.Container>
: outermost element to wrap all the pieces<Tabs.List>
: the tab bar that contains the tabs<Tabs.Item>
: each individual tab in the tab bar<Tabs.Panel>
: the content for each tab
There should be a 1-to-1 relationship between <Tabs.Item>
s and <Tabs.Panel>
s. The corresponding item/panel should have the same value.
All of the visual props, indicatorPosition
, spacing
, justifyContent
, should be placed on the <Tabs.List>
element. The other props like defaultValue
and isLazy
should go on the <Tabs.Container>
element.
import { Tabs } from '@aws-amplify/ui-react';
export const ComposedTabsExample = () => {
return (
<Tabs.Container defaultValue="Tab 1">
<Tabs.List>
<Tabs.Item value="Tab 1">Tab 1</Tabs.Item>
<Tabs.Item value="Tab 2">Tab 2</Tabs.Item>
<Tabs.Item value="Tab 3" isDisabled>
Tab 3
</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="Tab 1">Tab 1 content</Tabs.Panel>
<Tabs.Panel value="Tab 2">Tab 2 content</Tabs.Panel>
<Tabs.Panel value="Tab 3" isDisabled>
Tab 3 content
</Tabs.Panel>
</Tabs.Container>
);
};
With the composable interface you can add elements to the tabs like badges and icons
import { Badge, Icon, Tabs } from '@aws-amplify/ui-react';
const IconEmail = () => {
return (
<Icon
pathData="M22 6C22 4.9 21.1 4 20 4H4C2.9 4 2 4.9 2 6V18C2 19.1 2.9 20 4 20H20C21.1 20 22 19.1 22 18V6ZM20 6L12 11L4 6H20ZM20 18H4V8L12 13L20 8V18Z"
ariaLabel=""
/>
);
};
export const BadgeIcons = () => (
<Tabs.Container defaultValue="1">
<Tabs.List>
<Tabs.Item value="1">
Email{' '}
<Badge size="small" variation="info">
21
</Badge>
</Tabs.Item>
<Tabs.Item value="2">
<IconEmail /> Email
</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="1">Content of the first tab</Tabs.Panel>
<Tabs.Panel value="2">Content of the second tab</Tabs.Panel>
</Tabs.Container>
);
Styling
Theme
You can customize the appearance of all Tabs components in your application with a Theme.
Tabs Theme Source
import { Tabs, ThemeProvider, createTheme } from '@aws-amplify/ui-react';
const theme = createTheme({
name: 'tabs-theme',
tokens: {
components: {
tabs: {
borderColor: { value: '{colors.neutral.20}' },
item: {
color: { value: '{colors.blue.80}' },
fontSize: { value: '{fontSizes.xl}' },
fontWeight: { value: '{fontWeights.normal}' },
_hover: {
color: { value: '{colors.blue.60}' },
},
_focus: {
color: { value: '{colors.blue.60}' },
},
_active: {
color: { value: '{colors.blue.80}' },
borderColor: { value: '{colors.blue.80}' },
backgroundColor: { value: '{colors.blue.10}' },
},
_disabled: {
color: { value: 'gray' },
backgroundColor: { value: 'transparent' },
},
},
},
},
},
});
export const TabsThemeExample = () => {
return (
<ThemeProvider theme={theme} colorMode="light">
<Tabs.Container defaultValue="1">
<Tabs.List>
<Tabs.Item value="1">Tab 1</Tabs.Item>
<Tabs.Item value="2">Tab 2</Tabs.Item>
<Tabs.Item value="3" isDisabled>
Tab 3
</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="1">Tab 1 Content</Tabs.Panel>
<Tabs.Panel value="2">Tab 2 Content</Tabs.Panel>
<Tabs.Panel value="3" isDisabled>
Tab 3 Content
</Tabs.Panel>
</Tabs.Container>
</ThemeProvider>
);
};
Target classes
Class | Description |
---|---|
amplify-tabs | Top level element that wraps the Tabs primitive |
amplify-tabs__item | Tab element (button) in a TabPanel |
amplify-tabs__list | Top level element that wraps the TabItem primitive |
amplify-tabs__panel | Top level element that wraps the TabItem primitive |
--amplify-components-tabs-background-color
--amplify-components-tabs-border-color
--amplify-components-tabs-border-style
--amplify-components-tabs-border-width
--amplify-components-tabs-gap
--amplify-components-tabs-item-active-background-color
--amplify-components-tabs-item-active-border-color
--amplify-components-tabs-item-active-box-shadow
--amplify-components-tabs-item-active-color
--amplify-components-tabs-item-background-color
--amplify-components-tabs-item-border-color
--amplify-components-tabs-item-border-style
--amplify-components-tabs-item-border-width
--amplify-components-tabs-item-color
--amplify-components-tabs-item-disabled-background-color
--amplify-components-tabs-item-disabled-border-color
--amplify-components-tabs-item-disabled-box-shadow
--amplify-components-tabs-item-disabled-color
--amplify-components-tabs-item-focus-background-color
--amplify-components-tabs-item-focus-border-color
--amplify-components-tabs-item-focus-box-shadow
--amplify-components-tabs-item-focus-color
--amplify-components-tabs-item-font-size
--amplify-components-tabs-item-font-weight
--amplify-components-tabs-item-hover-background-color
--amplify-components-tabs-item-hover-border-color
--amplify-components-tabs-item-hover-box-shadow
--amplify-components-tabs-item-hover-color
--amplify-components-tabs-item-padding-horizontal
--amplify-components-tabs-item-padding-vertical
--amplify-components-tabs-item-text-align
--amplify-components-tabs-item-transition-duration
--amplify-components-tabs-panel-background-color
--amplify-components-tabs-panel-padding-block
--amplify-components-tabs-panel-padding-inline
Global styling
To override styling on all Tabs components, you can set Amplify CSS variables or use the target classes like .amplify-tabs
class.
/* styles.css */
[data-amplify-theme] {
/* background for the whole tab bar */
--amplify-components-tabs-background-color: var(
--amplify-colors-background-secondary
);
/* background for each tab */
--amplify-components-tabs-item-background-color: var(
--amplify-colors-background-primary
);
}
/* OR */
.amplify-tabs {
background-color: var(--amplify-colors-background-secondary);
}
.amplify-tabs__item {
background-color: var(--amplify-colors-background-primary);
}
To replace the Tabs styling, unset it:
.amplify-tabs {
all: unset;
/* Add your styling here*/
}
.amplify-tabs__item {
all: unset;
/* Add your styling here*/
}
Local styling
To override styling on a specific Tabs
component, you can use (in order of increasing specificity): a class selector or style props.
Using a class selector
/* styles.css */
.custom-tabs {
background-color: var(--amplify-colors-primary-60);
justify-content: center;
border-color: transparent;
}
.custom-tab-item {
background-color: var(--amplify-colors-primary-80);
color: var(--amplify-colors-font-inverse);
border-color: transparent;
}
.custom-tab-item:hover {
color: var(--amplify-colors-font-inverse);
background-color: var(--amplify-colors-primary-90);
}
.custom-tab-item[aria-selected='true'] {
color: var(--amplify-colors-font-inverse);
border-color: transparent;
background-color: var(--amplify-colors-primary-100);
}
Using style props
import { Tabs } from '@aws-amplify/ui-react';
export const StyleProps = () => {
return (
<Tabs.Container defaultValue="1">
<Tabs.List backgroundColor="background.secondary">
<Tabs.Item value="1" color="font.secondary">
Tab 1
</Tabs.Item>
<Tabs.Item value="2" color="secondary.60">
Tab 2
</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="1">Content of Tab 1</Tabs.Panel>
<Tabs.Panel value="2">Content of Tab 2</Tabs.Panel>
</Tabs.Container>
);
};
Note: there is currently no way to style different states like hover using only style props. However Amplify UI works well with CSS in JS frameworks for this usecase. Take a look at the alternative styling docs for information how to use CSS in JS with Amplify UI.
Accessibility
Adheres to the Tabs WAI-ARIA design pattern.
We recommend passing an ariaLabel
prop to help enable assistive technology.
import { Tabs } from '@aws-amplify/ui-react';
export const AriaLabel = () => (
<Tabs.Container ariaLabel="fruits" defaultValue="Apples">
<Tabs.List>
<Tabs.Item value="Apples">Apples</Tabs.Item>
<Tabs.Item value="Bananas">Bananas</Tabs.Item>
</Tabs.List>
<Tabs.Panel value="Apples">
Play with the Tab and Arrow keys to engage with this component.
</Tabs.Panel>
<Tabs.Panel value="Bananas">
Notice how an outline is added when the :focus-visible pseudo-class is
applied.
</Tabs.Panel>
</Tabs.Container>
);