Components
Tab Menu Horizontal
A horizontal pill-shaped tab menu with selected/unselected states. Supports neutral and primary variants, icons, number counter badges, and a composed shorthand.
Installation
bunx @happlyui/cli@latest add tab-menu-horizontal
Dependencies
npm packages
framer-motion
Registry dependencies
These are automatically installed when you add this component.
tvpolymorphicrecursive-clone-childrenbadge
Usage
import * as TabMenuHorizontal from "@/components/ui/tab-menu-horizontal"
<TabMenuHorizontal.Root>
<TabMenuHorizontal.Item selected>
<span>Overview</span>
</TabMenuHorizontal.Item>
<TabMenuHorizontal.Item>
<span>Details</span>
</TabMenuHorizontal.Item>
</TabMenuHorizontal.Root>
Examples
Default
Neutral variant with dark selected state.
<TabMenuHorizontal.Root>
<TabMenuHorizontal.Item selected={selected === 'overview'} onClick={() => setSelected('overview')}>
<span>Overview</span>
</TabMenuHorizontal.Item>
<TabMenuHorizontal.Item selected={selected === 'details'} onClick={() => setSelected('details')}>
<span>Details</span>
</TabMenuHorizontal.Item>
</TabMenuHorizontal.Root>
Primary
Primary variant using the brand color for the selected tab.
<TabMenuHorizontal.Root variant="primary">
<TabMenuHorizontal.Item selected>
<span>Overview</span>
</TabMenuHorizontal.Item>
</TabMenuHorizontal.Root>
With Icons
Tabs with leading icons using the polymorphic Icon subcomponent.
<TabMenuHorizontal.Item selected>
<TabMenuHorizontal.Icon as={RiHome5Line} />
<span>Home</span>
</TabMenuHorizontal.Item>
With Counter
Tabs with a red number counter badge, matching the design system badge pattern.
<TabMenuHorizontal.Item selected>
<span>My opportunities</span>
<TabMenuHorizontal.Counter count={3} />
</TabMenuHorizontal.Item>
Composed
Shorthand composed version with controlled value and optional count.
<TabMenuHorizontal.Composed
value={value}
onValueChange={setValue}
items={[
{ label: 'Overview', value: 'overview', icon: RiHome5Line },
{ label: 'Profile', value: 'profile', icon: RiUserLine, count: 5 },
]}
/>
Overflow
When tabs exceed the container width, the menu scrolls horizontally.
<div className="max-w-md">
<TabMenuHorizontal.Root>
<TabMenuHorizontal.Item selected>Dashboard</TabMenuHorizontal.Item>
<TabMenuHorizontal.Item>Analytics</TabMenuHorizontal.Item>
<TabMenuHorizontal.Item>Reports</TabMenuHorizontal.Item>
<!-- more tabs... -->
</TabMenuHorizontal.Root>
</div>
Disabled
Individual tabs can be disabled.
<TabMenuHorizontal.Item disabled>
<span>Disabled</span>
</TabMenuHorizontal.Item>
API Reference
TabMenuHorizontal.Root
Container for tab items with tablist role.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'neutral' | 'primary' | 'neutral' | Color scheme for selected tabs |
TabMenuHorizontal.Item
Individual tab button with pill shape.
| Prop | Type | Default | Description |
|---|---|---|---|
selected | boolean | false | Whether this tab is currently selected |
disabled | boolean | - | Disables the tab |
TabMenuHorizontal.Icon
Polymorphic icon inside a tab item.
| Prop | Type | Default | Description |
|---|---|---|---|
as | React.ElementType | 'div' | The element or component to render as |
TabMenuHorizontal.Counter
Red number badge inside a tab item. Hidden when count is 0 or less. Shows '99+' for values over 99.
| Prop | Type | Default | Description |
|---|---|---|---|
count | number | - | The number to display |
TabMenuHorizontal.Composed
Shorthand controlled tab menu. Accepts all Root props.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | The currently selected tab value |
onValueChange | (value: string) => void | - | Callback when a tab is clicked |
items | { label: string; value: string; disabled?: boolean; icon?: React.ElementType; count?: number }[] | - | Array of tab definitions |