Skip to main content
Reakit
DocumentationNewsletter
Spectrum
GitHub
GitHub

Tab

Accessible Tab component that follows the WAI-ARIA Tabs Pattern. It's a component that, when activated, display a TabPanel.

#Installation

npm install reakit

Learn more in Get started.

#Usage

import { useTabState, Tab, TabList, TabPanel } from "reakit/Tab";

function Example() {
  const tab = useTabState();
  return (
    <>
      <TabList {...tab} aria-label="My tabs">
        <Tab {...tab} stopId="tab1">
          Tab 1
        </Tab>
        <Tab {...tab} stopId="tab2" disabled>
          Tab 2
        </Tab>
        <Tab {...tab} stopId="tab3">
          Tab 3
        </Tab>
      </TabList>
      <TabPanel {...tab} stopId="tab1">
        Tab 1
      </TabPanel>
      <TabPanel {...tab} stopId="tab2">
        Tab 2
      </TabPanel>
      <TabPanel {...tab} stopId="tab3">
        Tab 3
      </TabPanel>
    </>
  );
}

#Default selected tab

You can set the default selected tab by passing a stopId to selectedId on useTabState.

Tab 3
import { useTabState, Tab, TabList, TabPanel } from "reakit/Tab";

function Example() {
  const tab = useTabState({ selectedId: "tab3" });
  return (
    <>
      <TabList {...tab} aria-label="My tabs">
        <Tab {...tab} stopId="tab1">
          Tab 1
        </Tab>
        <Tab {...tab} stopId="tab2" disabled>
          Tab 2
        </Tab>
        <Tab {...tab} stopId="tab3">
          Tab 3
        </Tab>
      </TabList>
      <TabPanel {...tab} stopId="tab1">
        Tab 1
      </TabPanel>
      <TabPanel {...tab} stopId="tab2">
        Tab 2
      </TabPanel>
      <TabPanel {...tab} stopId="tab3">
        Tab 3
      </TabPanel>
    </>
  );
}

#Manual activation

By default, a Tab is selected when it gets focused, which reveals its corresponding TabPanel. This behavior can be changed by setting manual to true on useTabState.

import { useTabState, Tab, TabList, TabPanel } from "reakit/Tab";

function Example() {
  const tab = useTabState({ manual: true });
  return (
    <>
      <TabList {...tab} aria-label="My tabs">
        <Tab {...tab} stopId="tab1">
          Tab 1
        </Tab>
        <Tab {...tab} stopId="tab2" disabled>
          Tab 2
        </Tab>
        <Tab {...tab} stopId="tab3">
          Tab 3
        </Tab>
      </TabList>
      <TabPanel {...tab} stopId="tab1">
        Tab 1
      </TabPanel>
      <TabPanel {...tab} stopId="tab2">
        Tab 2
      </TabPanel>
      <TabPanel {...tab} stopId="tab3">
        Tab 3
      </TabPanel>
    </>
  );
}

#Vertical tabs

You can control the orientation of the tabs by setting orientation on useTabState. Since it composes from Rover, explicitly defining an orientation will change how arrow key navigation works. If it's set to vertical, only and will work.

import { useTabState, Tab, TabList, TabPanel } from "reakit/Tab";

function Example() {
  const tab = useTabState({ orientation: "vertical" });
  return (
    <div style={{ display: "flex" }}>
      <TabList {...tab} aria-label="My tabs">
        <Tab {...tab} stopId="tab1">
          Tab 1
        </Tab>
        <Tab {...tab} stopId="tab2" disabled>
          Tab 2
        </Tab>
        <Tab {...tab} stopId="tab3">
          Tab 3
        </Tab>
      </TabList>
      <TabPanel {...tab} stopId="tab1">
        Tab 1
      </TabPanel>
      <TabPanel {...tab} stopId="tab2">
        Tab 2
      </TabPanel>
      <TabPanel {...tab} stopId="tab3">
        Tab 3
      </TabPanel>
    </div>
  );
}

#Abstracting

Like all other Reakit components, you can leverage the low level API to create your own customized API and make it less verbose, for example, by using React Context underneath.

Tab 3
import React from "react";
import {
  useTabState,
  Tab as BaseTab,
  TabList as BaseTabList,
  TabPanel as BaseTabPanel
} from "reakit/Tab";

const TabsContext = React.createContext();

function Tabs({ children, ...initialState }) {
  const tab = useTabState(initialState);
  const value = React.useMemo(() => tab, Object.values(tab));
  return <TabsContext.Provider value={value}>{children}</TabsContext.Provider>;
}

function Tab(props) {
  const tab = React.useContext(TabsContext);
  return <BaseTab {...tab} {...props} />;
}

function TabList(props) {
  const tab = React.useContext(TabsContext);
  return <BaseTabList {...tab} {...props} />;
}

function TabPanel(props) {
  const tab = React.useContext(TabsContext);
  return <BaseTabPanel {...tab} {...props} />;
}

function Example() {
  return (
    <Tabs selectedId="tab3">
      <TabList aria-label="My tabs">
        <Tab stopId="tab1">Tab 1</Tab>
        <Tab stopId="tab2">Tab 2</Tab>
        <Tab stopId="tab3">Tab 3</Tab>
      </TabList>
      <TabPanel stopId="tab1">Tab 1</TabPanel>
      <TabPanel stopId="tab2">Tab 2</TabPanel>
      <TabPanel stopId="tab3">Tab 3</TabPanel>
    </Tabs>
  );
}

#Accessibility

  • Tab has role tab.
  • Tab has aria-controls referring to its associated TabPanel.
  • The selected Tab has aria-selected set to true and all other Tabs have it set to false.
  • Tab extends the accessibility features of Rover.
  • TabList has role tablist.
  • TabList has aria-orientation set to vertical or horizontal based on the value of the orientation option.
  • TabPanel has role tabpanel.
  • TabPanel has aria-labelledby referring to its associated Tab.

Learn more in Accessibility.

#Composition

Learn more in Composition.

#Props

#useTabState

  • orientation "horizontal" | "vertical" | undefined

    Defines the orientation of the rover list.

  • stops Stop[]

    A list of element refs and IDs of the roving items.

  • currentId string | null

    The current focused element ID.

  • unstable_moves number

    Stores the number of moves that have been made by calling move, next, previous, first or last.

  • loop boolean

    If enabled:

    • Jumps to the first item when moving next from the last item.
    • Jumps to the last item when moving previous from the first item.
  • selectedId string | null

    The current selected tab's stopId.

  • manual boolean

    Whether the tab selection should be manual.

#Tab

  • disabled boolean | undefined

    Same as the HTML attribute.

  • focusable boolean | undefined

    When an element is disabled, it may still be focusable. It works similarly to readOnly on form elements. In this case, only aria-disabled will be set.

  • stopId string | undefined

    Element ID.

14 state props

These props are returned by the state hook. You can spread them into this component ({...state}) or pass them separately. You can also provide these props from your own state logic.

  • orientation "horizontal" | "vertical" | undefined

    Defines the orientation of the rover list.

  • unstable_moves number

    Stores the number of moves that have been made by calling move, next, previous, first or last.

  • stops Stop[]

    A list of element refs and IDs of the roving items.

  • currentId string | null

    The current focused element ID.

  • register (id: string, ref: RefObject<HTMLElement>) => void

    Registers the element ID and ref in the roving tab index list.

  • unregister (id: string) => void

    Unregisters the roving item.

  • move (id: string | null) => void

    Moves focus to a given element ID.

  • next () => void

    Moves focus to the next element.

  • previous () => void

    Moves focus to the previous element.

  • first () => void

    Moves focus to the first element.

  • last () => void

    Moves focus to the last element.

  • manual boolean

    Whether the tab selection should be manual.

  • selectedId string | null

    The current selected tab's stopId.

  • select (id: string | null) => void

    Selects a tab by its stopId.

#TabList

1 state props

These props are returned by the state hook. You can spread them into this component ({...state}) or pass them separately. You can also provide these props from your own state logic.

  • orientation "horizontal" | "vertical" | undefined

    Defines the orientation of the rover list.

#TabPanel

  • stopId string

    Tab's stopId.

4 state props

These props are returned by the state hook. You can spread them into this component ({...state}) or pass them separately. You can also provide these props from your own state logic.

  • visible boolean

    Whether it's visible or not.

  • unstable_animated number | boolean

    If true, animating will be set to true when visible changes. It'll wait for stopAnimation to be called or a CSS transition ends. If it's a number, stopAnimation will be called automatically after given milliseconds.

  • unstable_stopAnimation () => void

    Stops animation. It's called automatically if there's a CSS transition. It's called after given milliseconds if animated is a number.

  • selectedId string | null

    The current selected tab's stopId.