An accessible and easy tab component for ReactJS with full keyboard navigation and ARIA support
—
Full accessibility support with ARIA compliance, keyboard navigation, and focus management for screen readers and assistive technologies.
import { Tabs, TabList, Tab, TabPanel } from "react-tabs";Complete ARIA implementation following WAI-ARIA authoring practices for tab interfaces.
/**
* ARIA attributes automatically applied:
* - Tabs: container with data-rttabs identifier
* - TabList: role="tablist"
* - Tab: role="tab", aria-selected, aria-disabled, aria-controls
* - TabPanel: role="tabpanel", aria-labelledby
*/
interface ARIAAttributes {
/** Applied to TabList component */
'role': 'tablist';
/** Applied to Tab components */
'role': 'tab';
'aria-selected': 'true' | 'false';
'aria-disabled': 'true' | 'false';
'aria-controls': string; // References panel ID
/** Applied to TabPanel components */
'role': 'tabpanel';
'aria-labelledby': string; // References tab ID
}ARIA Structure Example:
<!-- Generated HTML structure with ARIA attributes -->
<div class="react-tabs" data-rttabs>
<ul role="tablist" class="react-tabs__tab-list">
<li role="tab"
id="tab0"
aria-selected="true"
aria-disabled="false"
aria-controls="panel0"
tabindex="0">
Tab 1
</li>
<li role="tab"
id="tab1"
aria-selected="false"
aria-disabled="false"
aria-controls="panel1"
tabindex="-1">
Tab 2
</li>
</ul>
<div role="tabpanel"
id="panel0"
aria-labelledby="tab0">
Panel 1 Content
</div>
<div role="tabpanel"
id="panel1"
aria-labelledby="tab1">
Panel 2 Content
</div>
</div>Full keyboard navigation support following standard tab interface patterns.
interface KeyboardNavigationProps {
/** Disable up/down arrow key navigation */
disableUpDownKeys?: boolean; // Default: false
/** Disable left/right arrow key navigation */
disableLeftRightKeys?: boolean; // Default: false
/** Text direction for navigation */
direction?: 'rtl' | 'ltr'; // Default: 'ltr'
}
/**
* Supported keyboard interactions:
* - Arrow Left/Up: Previous tab (respects RTL)
* - Arrow Right/Down: Next tab (respects RTL)
* - Home: First tab
* - End: Last tab
* - Enter/Space: Activate focused tab
* - Tab: Move to tab panel content
*/
interface KeyboardEvents {
ArrowLeft: () => void;
ArrowRight: () => void;
ArrowUp: () => void;
ArrowDown: () => void;
Home: () => void;
End: () => void;
Enter: () => void;
Space: () => void;
}Keyboard Navigation Examples:
// Standard keyboard navigation
<Tabs>
<TabList>
<Tab>Home</Tab>
<Tab>About</Tab>
<Tab>Contact</Tab>
</TabList>
<TabPanel>Home Content</TabPanel>
<TabPanel>About Content</TabPanel>
<TabPanel>Contact Content</TabPanel>
</Tabs>
// Disable vertical arrow keys (horizontal tabs only)
<Tabs disableUpDownKeys={true}>
<TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
</TabList>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
</Tabs>
// RTL support with reversed navigation
<Tabs direction="rtl">
<TabList>
<Tab>علامة التبويب 1</Tab>
<Tab>علامة التبويب 2</Tab>
</TabList>
<TabPanel>المحتوى 1</TabPanel>
<TabPanel>المحتوى 2</TabPanel>
</Tabs>
// Disable all arrow key navigation
<Tabs disableUpDownKeys={true} disableLeftRightKeys={true}>
<TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
</TabList>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
</Tabs>Comprehensive focus management for optimal keyboard and screen reader experience.
interface FocusManagementProps {
/** Focus the selected tab on initial render */
defaultFocus?: boolean; // Default: false
/** Focus tab when clicked */
focusTabOnClick?: boolean; // Default: true
/** Window environment for SSR compatibility */
environment?: Window;
}
/**
* Focus behavior:
* - Only selected tab is focusable (tabindex="0")
* - Other tabs have tabindex="-1"
* - Focus moves with selection via keyboard
* - Click focus can be disabled
*/
interface FocusBehavior {
selectedTabIndex: 0;
otherTabsIndex: -1;
movesFocusWithSelection: boolean;
respectsReducedMotion: boolean;
}Focus Management Examples:
// Auto-focus selected tab on mount
<Tabs defaultFocus={true}>
<TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
</TabList>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
</Tabs>
// Disable focus on click (for custom focus handling)
<Tabs focusTabOnClick={false}>
<TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
</TabList>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
</Tabs>
// Custom focus handling with refs
function CustomFocusExample() {
const tabsRef = useRef();
const focusTab = (index) => {
const tab = tabsRef.current.querySelector(`[aria-controls="panel${index}"]`);
if (tab) tab.focus();
};
return (
<Tabs
domRef={(node) => tabsRef.current = node}
focusTabOnClick={false}
>
<TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
</TabList>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
</Tabs>
);
}Accessible disabled tab implementation with proper ARIA attributes and navigation skipping.
interface DisabledTabProps {
/** Whether the tab is disabled */
disabled?: boolean; // Default: false
/** CSS class for disabled tabs */
disabledClassName?: string; // Default: "react-tabs__tab--disabled"
}
/**
* Disabled tab behavior:
* - aria-disabled="true" attribute
* - Skipped during keyboard navigation
* - Visual disabled styling applied
* - Click events prevented
*/
interface DisabledTabBehavior {
'aria-disabled': 'true';
clickable: false;
keyboardNavigable: false;
screenReaderAccessible: true;
}Disabled Tab Examples:
// Individual disabled tabs
<TabList>
<Tab>Available</Tab>
<Tab disabled>Coming Soon</Tab>
<Tab>Available</Tab>
</TabList>
// Conditional disabled state
<TabList>
<Tab>Public</Tab>
<Tab disabled={!user.isAdmin}>Admin Only</Tab>
<Tab disabled={!user.isPremium}>Premium Feature</Tab>
</TabList>
// Custom disabled styling
<Tabs disabledTabClassName="custom-disabled">
<TabList>
<Tab>Enabled</Tab>
<Tab disabled disabledClassName="special-disabled">
Special Disabled
</Tab>
</TabList>
<TabPanel>Enabled Content</TabPanel>
<TabPanel>This won't show</TabPanel>
</Tabs>Optimized experience for screen readers with proper announcements and navigation.
/**
* Screen reader optimizations:
* - Proper role attributes for semantic meaning
* - Live region updates for dynamic content
* - Descriptive labels and associations
* - Tab count and position announcements
*/
interface ScreenReaderFeatures {
semanticRoles: boolean;
liveRegionSupport: boolean;
labelAssociations: boolean;
positionAnnouncements: boolean;
}Screen Reader Enhancement Examples:
// Enhanced labeling for screen readers
<Tabs>
<TabList aria-label="Main navigation tabs">
<Tab aria-describedby="tab1-desc">
Dashboard
<span id="tab1-desc" className="sr-only">
Overview of your account
</span>
</Tab>
<Tab aria-describedby="tab2-desc">
Settings
<span id="tab2-desc" className="sr-only">
Account preferences and configuration
</span>
</Tab>
</TabList>
<TabPanel>
<h2>Dashboard</h2>
<p>Welcome to your account dashboard.</p>
</TabPanel>
<TabPanel>
<h2>Settings</h2>
<p>Manage your account settings.</p>
</TabPanel>
</Tabs>
// Dynamic content with live regions
function DynamicTabsExample() {
const [tabData, setTabData] = useState([]);
return (
<Tabs>
<TabList>
{tabData.map((tab, index) => (
<Tab key={tab.id}>
{tab.title}
{tab.hasUpdates && (
<span aria-live="polite" className="sr-only">
New updates available
</span>
)}
</Tab>
))}
</TabList>
{tabData.map((tab, index) => (
<TabPanel key={tab.id}>
<div aria-live="polite">
{tab.content}
</div>
</TabPanel>
))}
</Tabs>
);
}Right-to-left text direction support with appropriate keyboard navigation reversal.
interface RTLProps {
/** Text direction for RTL languages */
direction?: 'rtl' | 'ltr'; // Default: 'ltr'
}
/**
* RTL behavior:
* - Reverses left/right arrow key meanings
* - Maintains logical tab order
* - Compatible with CSS dir attribute
* - Preserves up/down navigation
*/
interface RTLBehavior {
arrowLeftAction: 'next' | 'previous'; // Depends on direction
arrowRightAction: 'next' | 'previous'; // Depends on direction
visualOrder: 'ltr' | 'rtl';
logicalOrder: 'consistent';
}RTL Support Examples:
// Arabic/Hebrew interface
<div dir="rtl">
<Tabs direction="rtl">
<TabList>
<Tab>الرئيسية</Tab>
<Tab>حول</Tab>
<Tab>اتصل</Tab>
</TabList>
<TabPanel>محتوى الرئيسية</TabPanel>
<TabPanel>محتوى حول</TabPanel>
<TabPanel>محتوى اتصل</TabPanel>
</Tabs>
</div>
// Dynamic direction based on content
function MultiLanguageTabs({ language }) {
const isRTL = ['ar', 'he', 'fa'].includes(language);
return (
<div dir={isRTL ? 'rtl' : 'ltr'}>
<Tabs direction={isRTL ? 'rtl' : 'ltr'}>
<TabList>
<Tab>{translations[language].home}</Tab>
<Tab>{translations[language].about}</Tab>
</TabList>
<TabPanel>{translations[language].homeContent}</TabPanel>
<TabPanel>{translations[language].aboutContent}</TabPanel>
</Tabs>
</div>
);
}Server-side rendering and custom environment support for accessibility features.
interface EnvironmentProps {
/** Window environment for SSR compatibility */
environment?: Window;
}
/**
* Environment handling for different runtime contexts:
* - SSR compatibility with no DOM access during server rendering
* - Custom window object support for iframe/popup contexts
* - IE compatibility workarounds for document.activeElement access bugs
* - Focus detection in non-standard environments
*/
interface EnvironmentSupport {
SSRCompatible: boolean;
customWindowSupport: boolean;
activeElementDetection: boolean;
IECompatibility: boolean;
}Environment Support Examples:
// Custom environment (e.g., iframe or popup window)
const iframeWindow = document.getElementById('myFrame').contentWindow;
<Tabs environment={iframeWindow}>
<TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
</TabList>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
</Tabs>
// IE compatibility workaround (fixes activeElement access in iframes)
const safeEnvironment = (() => {
try {
return window.document.activeElement ? window : undefined;
} catch (e) {
// IE throws error accessing activeElement in iframes
return undefined;
}
})();
<Tabs environment={safeEnvironment}>
{/* components */}
</Tabs>
// SSR-safe implementation
function SSRSafeTabs() {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
return (
<Tabs
environment={mounted ? window : undefined}
defaultFocus={false} // Avoid auto-focus on SSR
>
<TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
</TabList>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
</Tabs>
);
}Install with Tessl CLI
npx tessl i tessl/npm-react-tabs