OverflowTabs
freeOverflowTabs measures a tab strip against its available width and tucks whatever doesn't fit behind a "More" dropdown.
@floating-ui/react
Preview
Dark
Basic Info
Pricing
Inventory
Images
Shipping
SEO
Help
import { useState } from 'react';
import OverflowTabs from '@/components/composites/OverflowTabs';
const sections = [
{ label: 'Basic Info', value: 'basic' },
{ label: 'Pricing', value: 'pricing' },
{ label: 'Inventory', value: 'inventory' },
{ label: 'Images', value: 'images' },
{ label: 'Shipping', value: 'shipping' },
{ label: 'SEO', value: 'seo' },
{ label: 'Help', value: 'help' },
];
export default function ValidationDemo() {
const [value, setValue] = useState('basic');
return (
<OverflowTabs
tabList={sections.map((section) => ({
value: section.value,
label: section.label,
}))}
value={value}
onChange={setValue}
/>
);
}Installation
Add this component with the NateUI CLI.
npx nateui@latest add OverflowTabsExamples
With Actions
Preview
Dark
Details
Comments
History
Attachments
Activity
import { useState } from 'react';
import { PiPlus } from 'react-icons/pi'
import Button from '@/components/ui/Button';
import OverflowTabs from '@/components/composites/OverflowTabs';
const tabList = [
{ label: 'Details', value: 'details' },
{ label: 'Comments', value: 'comments' },
{ label: 'History', value: 'history' },
{ label: 'Attachments', value: 'attachments' },
{ label: 'Activity', value: 'activity' },
];
export default function WithActionsDemo() {
const [value, setValue] = useState('details');
return (
<OverflowTabs
tabList={tabList}
value={value}
onChange={setValue}
tabListClass="border-0"
className="border-b "
>
<Button size="sm" icon={<PiPlus />}>
New
</Button>
</OverflowTabs>
);
}Icon Labels
Preview
Dark
General
Members
Roles & Access
Billing
Webhooks
Audit Log
import { useState } from 'react';
import {
PiClockCounterClockwise,
PiCreditCard,
PiShieldCheck,
PiSquaresFour,
PiUsers,
PiWebhooksLogo,
} from 'react-icons/pi'
import OverflowTabs from '@/components/composites/OverflowTabs';
const sections = [
{ label: 'General', value: 'general', icon: <PiSquaresFour /> },
{ label: 'Members', value: 'members', icon: <PiUsers /> },
{ label: 'Roles & Access', value: 'roles', icon: <PiShieldCheck /> },
{ label: 'Billing', value: 'billing', icon: <PiCreditCard /> },
{ label: 'Webhooks', value: 'webhooks', icon: <PiWebhooksLogo /> },
{ label: 'Audit Log', value: 'audit', icon: <PiClockCounterClockwise /> },
];
export default function IconLabelsDemo() {
const [value, setValue] = useState('general');
return (
<OverflowTabs
tabList={sections.map((section) => ({
value: section.value,
label: (
<span className="flex items-center gap-2">
{section.icon}
{section.label}
</span>
),
}))}
value={value}
onChange={setValue}
tabNavClass="min-w-[100px] text-center"
/>
);
}API
| Prop | Description | Type | Default |
|---|---|---|---|
tabList | Tabs to render. label accepts any ReactNode, so icons or inline state indicators compose directly into it — there's no separate icon field (see Icon Labels). | OverflowTabsTab[] | — |
value | Currently active tab. When the active tab has overflowed into the dropdown, the visible tab strip shows no active indicator and the "More" button takes its place instead. | string | — |
onChange | Called with the new tab's value, whether it's clicked in the visible strip or in the overflow dropdown. | (value: string) => void | — |
children | Extra content docked after the tabs, such as an action button. Its width is included in the overflow calculation. | ReactNode | — |
tabListClass | Class names applied to the tab list container. | string | — |
tabNavClass | Class names applied to each visible tab. | string | — |
className | Class names applied to the inner Tabs root, not the outer container. | string | — |
...rest | Native div props, applied to the outer container. | NativeOverflowTabsDivProps | — |
Types
type OverflowTabsTab = {
label: string | ReactNode;
value: string;
}
type NativeOverflowTabsDivProps = Omit<ComponentProps<'div'>, 'onChange'>