HapplyUI

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.

  • tv
  • polymorphic
  • recursive-clone-children
  • badge

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.

PropTypeDefaultDescription
variant'neutral' | 'primary''neutral'Color scheme for selected tabs

TabMenuHorizontal.Item

Individual tab button with pill shape.

PropTypeDefaultDescription
selectedbooleanfalseWhether this tab is currently selected
disabledboolean-Disables the tab

TabMenuHorizontal.Icon

Polymorphic icon inside a tab item.

PropTypeDefaultDescription
asReact.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.

PropTypeDefaultDescription
countnumber-The number to display

TabMenuHorizontal.Composed

Shorthand controlled tab menu. Accepts all Root props.

PropTypeDefaultDescription
valuestring-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

Previous
Switch Toggle
Next
Alert