Tabs Component
The Tabs component is a flexible, fully responsive UI primitive built with TypeScript. It supports multiple layouts, visual variants, keyboard navigation, and mobile-aware behaviors — making it ideal for dashboards, settings panels, and structured content navigation.
Use variant and position to control layout and styling, mobileVariant for responsive behavior, and TabList + TabPanels for structured composition. Customize appearance with semantic theme classes, CSS variables, className, and layout props like radius.
Import Component From Library
1import { Tabs, Tab, TabList, TabPanels, TabPanel } from "@neuctra/ui";Basic Usage Code
Start with a simple tab system. The component provides flexible tab navigation with multiple layouts and responsive behavior.
1import { Tabs, TabList, Tab, TabPanels, TabPanel } from '@neuctra/ui';
2
3function BasicExample() {
4 return (
5 <Tabs>
6 <TabList>
7 <Tab index={0}>Overview</Tab>
8 <Tab index={1}>Settings</Tab>
9 <Tab index={2}>Profile</Tab>
10 </TabList>
11 <TabPanels>
12 <TabPanel index={0}>
13 <div className="p-6">Overview content</div>
14 </TabPanel>
15 <TabPanel index={1}>
16 <div className="p-6">Settings content</div>
17 </TabPanel>
18 <TabPanel index={2}>
19 <div className="p-6">Profile content</div>
20 </TabPanel>
21 </TabPanels>
22 </Tabs>
23 );
24}Basic Usage
Tabs Props
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | TabList and TabPanels composition |
| defaultActive | number | 0 | Initial active tab index |
| position | "top" | "bottom" | "left" | "right" | "top" | Tabs layout position |
| variant | "solid" | "outline" | "underline" | "pill" | "solid" | Visual style of tabs |
| fullWidth | boolean | false | Makes tabs stretch full width |
| radius | number | 8 | Border radius of tabs |
| transitionDuration | number | 200 | Animation duration in ms |
| bordered | boolean | false | Adds outer border wrapper |
| tabCount | number | auto | Manually define tab count |
| mobileBreakpoint | number | 768 | Breakpoint for mobile behavior |
| mobileVariant | "drawer" | "scroll" | "stack" | "collapse" | "scroll" | Mobile layout behavior |
| primaryColor | string | "var(--primary)" | Context color token for custom tab extensions |
| activeColor | string | "var(--foreground)" | Context color token for custom tab extensions |
| textColor | string | "" | Context color token for custom tab extensions |
| hoverColor | string | "var(--primary)" | Context color token for custom tab extensions |
| borderColor | string | "var(--border)" | Context color token for custom tab extensions |
| disabledColor | string | "var(--muted-foreground)" | Context color token for custom tab extensions |
| backgroundColor | string | — | Root container background color (falls back to var(--background)) |
| onTabChange | (index: number) => void | — | Triggered when tab changes |
| className | string | — | Root wrapper class |
| style | React.CSSProperties | — | Root inline styles |
1<TabList gap={8} drawerLabel="Select tab" />TabList Props
| Prop | Type | Default | Description |
|---|---|---|---|
| gap | number | 8 | Spacing between tabs |
| drawerLabel | ReactNode | "Select tab" | Label shown in mobile drawer trigger |
| drawerIcon | ReactNode | — | Accepted for a custom drawer chevron icon, but not yet rendered (the built-in chevron is always used) |
| className | string | — | Wrapper class styling |
| style | React.CSSProperties | — | Inline styles for TabList |
| triggerClassName | string | — | Styles the drawer trigger button (mobileVariant="drawer" only) |
| menuClassName | string | — | Styles the dropdown menu panel (mobileVariant="drawer" only) |
1<Tab icon={<Icon />} disabled={false}>Tab 1</Tab>Tab Props
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | Tab label content |
| icon | ReactNode | — | Optional icon |
| disabled | boolean | false | Disable tab interaction |
| ariaLabel | string | — | Accessibility label |
| index | number | auto | Manual tab index override |
| className | string | — | Tab button styling |
| iconClassName | string | — | Styles the wrapper around the icon node |
| style | React.CSSProperties | — | Accepted by the type, but not currently applied by the component |
| activeStyle | React.CSSProperties | — | Accepted by the type, but not currently applied by the component |
| inactiveStyle | React.CSSProperties | — | Accepted by the type, but not currently applied by the component |
1<TabPanels>
2 <TabPanel index={0}>Content</TabPanel>
3</TabPanels>TabPanels Props
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | Wrapper for multiple TabPanel components |
| className | string | — | Custom class for panels container |
| style | React.CSSProperties | — | Inline styles for panels wrapper |
1<TabPanel index={0} keepMounted>
2 Content
3</TabPanel>TabPanel Props
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | Panel content |
| index | number | — | Tab index it belongs to |
| keepMounted | boolean | false | Keep in DOM when inactive |
| className | string | — | Panel wrapper class |
| style | React.CSSProperties | — | Inline styles |
Common Mistakes
<TabPanel>Content</TabPanel>Each TabPanel must have a matching index
<TabList><Tab /> x3</TabList> + <TabPanel /> x2Number of Tabs and TabPanels should match
<Tab index={5} />Avoid manually setting index unless necessary — auto indexing is handled internally
<TabPanel outside <Tabs> />All components must be inside <Tabs> provider
<Tab disabled /> + expecting clickDisabled tabs do not trigger setActive
<Tabs><TabList /><TabPanels /></Tabs>Always follow the correct structure hierarchy
Pro Tips
- Use
variant="underline"for clean navigation-style tabs, andpillfor dashboards. - Combine
position="left"withfullWidthfor sidebar layouts. - Use
mobileVariant="drawer"for better UX on small screens. - Prefer
keepMountedwhen tab content has expensive re-renders. - Use
onTabChangeto sync tabs with routing or state (e.g. query params). - Add
iconin<Tab>for better visual scanning. - Use
fullWidthfor equal tab sizing in forms or mobile layouts. - Customize with CSS variables (
--primary,--accent) instead of inline styles. - Avoid heavy content inside tabs unless using
keepMounted. - Keyboard navigation is built-in — ensure focus styles are visible for accessibility.
Frequently Asked Questions
How do I install and import the Tabs component?
Install the @neuctra/ui package, then import it with: import { Tabs } from "@neuctra/ui";
What variants and sizes does Tabs support?
Tabs supports variants: solid, outline, underline, pill. Use the variant and size props to switch between them, or pass a className to layer on custom Tailwind CSS classes.
Is the Tabs component accessible?
Tabs is built with semantic HTML and standard ARIA attributes, and supports keyboard navigation and focus states out of the box, so it works well with screen readers and keyboard-only users.