Amplify UI

Tabs

Tabs allow users to navigate between sections of related content.

Demo

Usage

The Tabs component had some breaking changes in v6. Please see the migration docs for more information.

Import the Tabs component and pass an array of items and a defaultValue.

Tab content #1
Tab content #2
Tab content #3
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.

Always include a defaultValue or value prop. If both are undefined, no initial tab is selected!

Content of the first tab

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.

Tab 1 content
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.

Content of the first tab
Content of the second tab
Content of the third tab
Content of the first tab
Content of the second tab
Content of the third tab
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.

Tabs are centered
Content of the second tab
Content of the third tab
Tabs are stacked to the right
Content of the second tab
Content of the third tab
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).

Tab 1 content
Tab 2 content
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.

Tab 1 Content
Tab 2 Content
This Tab is not clickable and will not display its content 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.

Tab 1 content
Tab 1 content
Tab 1 content
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 1 content</Tabs.Panel>
      <Tabs.Panel value="Tab 3" isDisabled>
        Tab 1 content
      </Tabs.Panel>
    </Tabs.Container>
  );
};

With the composable interface you can add elements to the tabs like badges and icons

Content of the first tab
Content of the second tab
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

Tab 1 Content
Tab 2 Content
Tab 3 Content
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

ClassDescription
amplify-tabsTop level element that wraps the Tabs primitive
amplify-tabs__itemTab element (button) in a TabPanel
amplify-tabs__listTop level element that wraps the TabItem primitive
amplify-tabs__panelTop 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.

Tab content #1
Tab content #2
Tab content #3
/* 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

Content of Tab 1
Content of Tab 2
/* 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

Content of Tab 1
Content of Tab 2
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.

Play with the Tab and Arrow keys to engage with this component.
Notice how an outline is added when the :focus-visible pseudo-class is applied.
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>
);

Amplify open source software, documentation and community are supported by Amazon Web Services.

© 2024 Amazon Web Services, Inc. and its affiliates. All rights reserved. View the site terms and privacy policy.

Flutter and the related logo are trademarks of Google LLC. We are not endorsed by or affiliated with Google LLC.