An accessible and easy tab component for ReactJS with full keyboard navigation and ARIA support
—
Complete set of accessible React components for building tabbed interfaces with keyboard navigation and ARIA compliance.
import { Tabs, TabList, Tab, TabPanel } from "react-tabs";Main container component that manages tab state, handles keyboard navigation, and provides controlled/uncontrolled behavior.
/**
* Main container component for tab functionality
* Manages state, keyboard navigation, and accessibility
* @param props - TabsProps configuration object
* @returns JSX element rendering as div with data-rttabs attribute
*/
const Tabs: React.FunctionComponent<TabsProps>;
interface TabsProps extends Omit<HTMLProps<HTMLDivElement>, 'className' | 'onSelect' | 'ref'> {
/** CSS class name(s) for the container */
className?: string | string[] | { [name: string]: boolean };
/** Focus tabs on initial render */
defaultFocus?: boolean;
/** Initial tab index for uncontrolled mode */
defaultIndex?: number;
/** Text direction for RTL support */
direction?: 'rtl' | 'ltr';
/** CSS class for disabled tabs */
disabledTabClassName?: string;
/** Disable up/down arrow key navigation */
disableUpDownKeys?: boolean;
/** Disable left/right arrow key navigation */
disableLeftRightKeys?: boolean;
/** Ref callback for the root DOM element */
domRef?: ((node?: HTMLElement) => void);
/** Window environment for SSR compatibility */
environment?: Window;
/** Focus tab when clicked */
focusTabOnClick?: boolean;
/** Force render all tab panels */
forceRenderTabPanel?: boolean;
/** Selection change callback */
onSelect?: ((index: number, last: number, event: Event) => boolean | void);
/** Selected tab index for controlled mode */
selectedIndex?: number;
/** CSS class for selected tab */
selectedTabClassName?: string;
/** CSS class for selected tab panel */
selectedTabPanelClassName?: string;
}Usage Examples:
// Uncontrolled mode with default settings
<Tabs>
<TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
</TabList>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
</Tabs>
// Controlled mode
const [selectedIndex, setSelectedIndex] = useState(0);
<Tabs
selectedIndex={selectedIndex}
onSelect={(index) => setSelectedIndex(index)}
>
<TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
</TabList>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
</Tabs>
// With custom styling and initial focus
<Tabs
className="my-tabs"
defaultIndex={1}
defaultFocus={true}
selectedTabClassName="active-tab"
>
<TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
</TabList>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
</Tabs>Container for Tab components that renders as an accessible <ul> element with role="tablist".
/**
* Container component for Tab elements
* Renders as ul element with role="tablist"
* @param props - TabListProps configuration object
* @returns JSX element rendering as ul with role="tablist"
*/
const TabList: React.FunctionComponent<TabListProps>;
interface TabListProps extends Omit<HTMLProps<HTMLUListElement>, 'className'> {
/** CSS class name(s) for the tab list */
className?: string | string[] | { [name: string]: boolean };
}Usage Examples:
// Basic tab list
<TabList>
<Tab>First Tab</Tab>
<Tab>Second Tab</Tab>
<Tab>Third Tab</Tab>
</TabList>
// With custom styling
<TabList className="custom-tab-list">
<Tab>First Tab</Tab>
<Tab>Second Tab</Tab>
</TabList>
// With multiple class names
<TabList className={["tab-list", "horizontal", { "dark-theme": isDark }]}>
<Tab>First Tab</Tab>
<Tab>Second Tab</Tab>
</TabList>Individual tab elements that render as <li> elements with role="tab" and full ARIA attributes.
/**
* Individual tab element with accessibility features
* Renders as li element with role="tab" and ARIA attributes
* @param props - TabProps configuration object
* @returns JSX element rendering as li with role="tab"
*/
const Tab: React.FunctionComponent<TabProps>;
interface TabProps extends Omit<HTMLProps<HTMLLIElement>, 'className' | 'tabIndex'> {
/** CSS class name(s) for the tab */
className?: string | string[] | { [name: string]: boolean };
/** Whether the tab is disabled */
disabled?: boolean;
/** CSS class for disabled state */
disabledClassName?: string;
/** CSS class for selected state */
selectedClassName?: string;
/** Tab index for keyboard navigation */
tabIndex?: string;
}Usage Examples:
// Basic tabs
<Tab>Home</Tab>
<Tab>About</Tab>
<Tab>Contact</Tab>
// Disabled tab
<Tab disabled>Coming Soon</Tab>
// With custom styling
<Tab
className="custom-tab"
selectedClassName="tab-active"
disabledClassName="tab-disabled"
>
Dashboard
</Tab>
// With event handlers
<Tab
onClick={handleTabClick}
onKeyDown={handleKeyDown}
>
Settings
</Tab>Content areas that render as <div> elements with role="tabpanel" and conditional visibility.
/**
* Content area for tab panels with conditional rendering
* Renders as div element with role="tabpanel" and ARIA attributes
* @param props - TabPanelProps configuration object
* @returns JSX element rendering as div with role="tabpanel"
*/
const TabPanel: React.FunctionComponent<TabPanelProps>;
interface TabPanelProps extends Omit<HTMLProps<HTMLDivElement>, 'className'> {
/** CSS class name(s) for the panel */
className?: string | string[] | { [name: string]: boolean };
/** Force render even when not selected */
forceRender?: boolean;
/** CSS class for selected state */
selectedClassName?: string;
}Usage Examples:
// Basic tab panels
<TabPanel>
<h2>Welcome to our homepage</h2>
<p>This is the main content area.</p>
</TabPanel>
<TabPanel>
<h2>About Us</h2>
<p>Learn more about our company.</p>
</TabPanel>
// Force render panel (keeps content in DOM when not selected)
<TabPanel forceRender={true}>
<VideoPlayer autoPlay={false} />
</TabPanel>
// With custom styling
<TabPanel
className="custom-panel"
selectedClassName="panel-active"
>
<ComplexComponent />
</TabPanel>
// With conditional content
<TabPanel>
{isLoading ? (
<LoadingSpinner />
) : (
<DataTable data={data} />
)}
</TabPanel>All four components must be used together in the correct hierarchy:
<Tabs> {/* Root container */}
<TabList> {/* Tab navigation */}
<Tab>Tab 1</Tab> {/* Individual tabs */}
<Tab>Tab 2</Tab>
</TabList>
<TabPanel> {/* Content panels */}
Content 1
</TabPanel>
<TabPanel>
Content 2
</TabPanel>
</Tabs>Each component includes a tabsRole static property for internal identification:
Tabs.tabsRole = 'Tabs';
TabList.tabsRole = 'TabList';
Tab.tabsRole = 'Tab';
TabPanel.tabsRole = 'TabPanel';These properties enable the library to validate component structure and ensure proper nesting.
React Tabs automatically adds data attributes for testing and internal identification:
/**
* Data attributes added by the library
*/
interface DataAttributes {
/** Added to the main Tabs container */
'data-rttabs': boolean;
/** Added to each Tab element */
'data-rttab': boolean;
}Testing Usage:
// Jest/Testing Library selectors
const tabsContainer = screen.getByTestId('tabs-container');
// Or using data attributes
const tabsContainer = document.querySelector('[data-rttabs]');
const firstTab = document.querySelector('[data-rttab]');
// Cypress selectors
cy.get('[data-rttabs]').should('exist');
cy.get('[data-rttab]').first().click();Install with Tessl CLI
npx tessl i tessl/npm-react-tabs