Tabs organize content into multiple sections and allow users to navigate between them
Import
import { Tabs } from "heroui-solid";Usage
View your project overview and recent activity.
Track your metrics and analyze performance data.
Generate and download detailed reports.
import { Tabs } from "heroui-solid"
export function Basic() {
return (
<Tabs class="w-full max-w-md">
<Tabs.ListContainer>
<Tabs.List aria-label="Options">
<Tabs.Tab id="overview">
Overview
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="analytics">
Analytics
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="reports">
Reports
<Tabs.Indicator />
</Tabs.Tab>
</Tabs.List>
</Tabs.ListContainer>
<Tabs.Panel class="pt-4" id="overview">
<p>View your project overview and recent activity.</p>
</Tabs.Panel>
<Tabs.Panel class="pt-4" id="analytics">
<p>Track your metrics and analyze performance data.</p>
</Tabs.Panel>
<Tabs.Panel class="pt-4" id="reports">
<p>Generate and download detailed reports.</p>
</Tabs.Panel>
</Tabs>
)
}Anatomy
Import the Tabs component and access all parts using dot notation.
import { Tabs } from "heroui-solid";
export default () => (
<Tabs>
<Tabs.ListContainer>
<Tabs.List aria-label="Options">
<Tabs.Tab>
<Tabs.Separator /> {/* Optional */}
<Tabs.Indicator />
</Tabs.Tab>
</Tabs.List>
</Tabs.ListContainer>
<Tabs.Panel />
</Tabs>
);Vertical
Account Settings
Manage your account information and preferences.
Security Settings
Configure two-factor authentication and password settings.
Notification Preferences
Choose how and when you want to receive notifications.
Billing Information
View and manage your subscription and payment methods.
import { Tabs } from "heroui-solid"
export function Vertical() {
return (
<Tabs class="w-full max-w-lg" orientation="vertical">
<Tabs.ListContainer>
<Tabs.List aria-label="Vertical tabs">
<Tabs.Tab id="account">
Account
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="security">
Security
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="notifications">
Notifications
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="billing">
Billing
<Tabs.Indicator />
</Tabs.Tab>
</Tabs.List>
</Tabs.ListContainer>
<Tabs.Panel class="px-4" id="account">
<h3 class="mb-2 font-semibold">Account Settings</h3>
<p class="text-sm text-muted">
Manage your account information and preferences.
</p>
</Tabs.Panel>
<Tabs.Panel class="px-4" id="security">
<h3 class="mb-2 font-semibold">Security Settings</h3>
<p class="text-sm text-muted">
Configure two-factor authentication and password settings.
</p>
</Tabs.Panel>
<Tabs.Panel class="px-4" id="notifications">
<h3 class="mb-2 font-semibold">Notification Preferences</h3>
<p class="text-sm text-muted">
Choose how and when you want to receive notifications.
</p>
</Tabs.Panel>
<Tabs.Panel class="px-4" id="billing">
<h3 class="mb-2 font-semibold">Billing Information</h3>
<p class="text-sm text-muted">
View and manage your subscription and payment methods.
</p>
</Tabs.Panel>
</Tabs>
)
}Overflow
When the tab list exceeds the available space, Tabs.ListContainer automatically renders scroll chevrons and fading edges so users can navigate the hidden tabs. This works the same way for both horizontal and vertical orientations. When the list is known to overflow, pass initialShadow to Tabs.ListContainer so the chevron and fade are already in the server-rendered HTML instead of appearing after hydration.
Overview panel content.
Analytics panel content.
Reports panel content.
Performance panel content.
Engagement panel content.
Audience panel content.
Acquisition panel content.
Retention panel content.
Settings panel content.
import { Tabs } from "heroui-solid"
import { For } from "solid-js"
const items = [
{ id: "overview", label: "Overview" },
{ id: "analytics", label: "Analytics" },
{ id: "reports", label: "Reports" },
{ id: "performance", label: "Performance" },
{ id: "engagement", label: "Engagement" },
{ id: "audience", label: "Audience" },
{ id: "acquisition", label: "Acquisition" },
{ id: "retention", label: "Retention" },
{ id: "settings", label: "Settings" }
]
export function Overflow() {
return (
<div class="w-[400px]">
<Tabs>
<Tabs.ListContainer initialShadow>
<Tabs.List aria-label="Overflow options">
<For each={items}>
{(item) => (
<Tabs.Tab id={item.id}>
{item.label}
<Tabs.Indicator />
</Tabs.Tab>
)}
</For>
</Tabs.List>
</Tabs.ListContainer>
<For each={items}>
{(item) => (
<Tabs.Panel class="pt-4" id={item.id}>
<p>{item.label} panel content.</p>
</Tabs.Panel>
)}
</For>
</Tabs>
</div>
)
}Disabled Tab
This tab is active and can be selected.
This content cannot be accessed.
This tab is also available for selection.
import { Tabs } from "heroui-solid"
export function Disabled() {
return (
<Tabs class="w-full max-w-md">
<Tabs.ListContainer>
<Tabs.List aria-label="Tabs with disabled">
<Tabs.Tab id="active">
Active
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab isDisabled id="disabled">
Disabled
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="available">
Available
<Tabs.Indicator />
</Tabs.Tab>
</Tabs.List>
</Tabs.ListContainer>
<Tabs.Panel class="pt-4" id="active">
<p>This tab is active and can be selected.</p>
</Tabs.Panel>
<Tabs.Panel class="pt-4" id="disabled">
<p>This content cannot be accessed.</p>
</Tabs.Panel>
<Tabs.Panel class="pt-4" id="available">
<p>This tab is also available for selection.</p>
</Tabs.Panel>
</Tabs>
)
}With Separator
Add <Tabs.Separator /> inside each <Tabs.Tab> (except the first) to display separator lines between tabs.
View your project overview and recent activity.
Track your metrics and analyze performance data.
Generate and download detailed reports.
import { Tabs } from "heroui-solid"
export function WithSeparator() {
return (
<Tabs class="w-full max-w-md">
<Tabs.ListContainer>
<Tabs.List aria-label="Options">
<Tabs.Tab id="overview">
Overview
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="analytics">
<Tabs.Separator />
Analytics
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="reports">
<Tabs.Separator />
Reports
<Tabs.Indicator />
</Tabs.Tab>
</Tabs.List>
</Tabs.ListContainer>
<Tabs.Panel class="pt-4" id="overview">
<p>View your project overview and recent activity.</p>
</Tabs.Panel>
<Tabs.Panel class="pt-4" id="analytics">
<p>Track your metrics and analyze performance data.</p>
</Tabs.Panel>
<Tabs.Panel class="pt-4" id="reports">
<p>Generate and download detailed reports.</p>
</Tabs.Panel>
</Tabs>
)
}Custom Styles
import { Tabs } from "heroui-solid"
export function CustomStyles() {
return (
<Tabs class="w-full max-w-lg text-center">
<Tabs.ListContainer>
<Tabs.List
aria-label="Options"
class="w-fit *:h-6 *:w-fit *:px-3 *:text-sm *:font-normal *:data-[selected=true]:text-accent-foreground"
>
<Tabs.Tab id="daily">
Daily
<Tabs.Indicator class="bg-accent" />
</Tabs.Tab>
<Tabs.Tab id="weekly">
Weekly
<Tabs.Indicator class="bg-accent" />
</Tabs.Tab>
<Tabs.Tab id="bi-weekly">
Bi-Weekly
<Tabs.Indicator class="bg-accent" />
</Tabs.Tab>
<Tabs.Tab id="monthly">
Monthly
<Tabs.Indicator class="bg-accent" />
</Tabs.Tab>
</Tabs.List>
</Tabs.ListContainer>
</Tabs>
)
}Secondary Variant
View your project overview and recent activity.
Track your metrics and analyze performance data.
Generate and download detailed reports.
import { Tabs } from "heroui-solid"
export function Secondary() {
return (
<Tabs class="w-full max-w-md" variant="secondary">
<Tabs.ListContainer>
<Tabs.List aria-label="Options">
<Tabs.Tab id="overview">
Overview
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="analytics">
Analytics
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="reports">
Reports
<Tabs.Indicator />
</Tabs.Tab>
</Tabs.List>
</Tabs.ListContainer>
<Tabs.Panel class="pt-4" id="overview">
<p>View your project overview and recent activity.</p>
</Tabs.Panel>
<Tabs.Panel class="pt-4" id="analytics">
<p>Track your metrics and analyze performance data.</p>
</Tabs.Panel>
<Tabs.Panel class="pt-4" id="reports">
<p>Generate and download detailed reports.</p>
</Tabs.Panel>
</Tabs>
)
}Secondary Variant Vertical
Account Settings
Manage your account information and preferences.
Security Settings
Configure two-factor authentication and password settings.
Notification Preferences
Choose how and when you want to receive notifications.
Billing Information
View and manage your subscription and payment methods.
import { Tabs } from "heroui-solid"
export function SecondaryVertical() {
return (
<Tabs class="w-full max-w-lg" orientation="vertical" variant="secondary">
<Tabs.ListContainer>
<Tabs.List aria-label="Vertical tabs">
<Tabs.Tab id="account">
Account
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="security">
Security
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="notifications">
Notifications
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="billing">
Billing
<Tabs.Indicator />
</Tabs.Tab>
</Tabs.List>
</Tabs.ListContainer>
<Tabs.Panel class="px-4" id="account">
<h3 class="mb-2 font-semibold">Account Settings</h3>
<p class="text-sm text-muted">
Manage your account information and preferences.
</p>
</Tabs.Panel>
<Tabs.Panel class="px-4" id="security">
<h3 class="mb-2 font-semibold">Security Settings</h3>
<p class="text-sm text-muted">
Configure two-factor authentication and password settings.
</p>
</Tabs.Panel>
<Tabs.Panel class="px-4" id="notifications">
<h3 class="mb-2 font-semibold">Notification Preferences</h3>
<p class="text-sm text-muted">
Choose how and when you want to receive notifications.
</p>
</Tabs.Panel>
<Tabs.Panel class="px-4" id="billing">
<h3 class="mb-2 font-semibold">Billing Information</h3>
<p class="text-sm text-muted">
View and manage your subscription and payment methods.
</p>
</Tabs.Panel>
</Tabs>
)
}Custom Element
HeroUI React overrides the rendered elements with a render prop. This port
uses Kobalte's polymorphic as prop instead — for example, rendering each tab
as an anchor:
Each tab renders as an anchor via Kobalte's polymorphic as prop.
Track your metrics and analyze performance data.
Generate and download detailed reports.
import { Tabs } from "heroui-solid"
export function CustomElement() {
return (
<Tabs class="w-full max-w-md">
<Tabs.ListContainer>
<Tabs.List aria-label="Options">
<Tabs.Tab as="a" href="#getting-started" id="getting-started">
Getting Started
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab as="a" href="#components" id="components">
Components
<Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab as="a" href="#releases" id="releases">
Releases
<Tabs.Indicator />
</Tabs.Tab>
</Tabs.List>
</Tabs.ListContainer>
<Tabs.Panel class="pt-4" id="getting-started">
<p>Each tab renders as an anchor via Kobalte's polymorphic as prop.</p>
</Tabs.Panel>
<Tabs.Panel class="pt-4" id="components">
<p>Track your metrics and analyze performance data.</p>
</Tabs.Panel>
<Tabs.Panel class="pt-4" id="releases">
<p>Generate and download detailed reports.</p>
</Tabs.Panel>
</Tabs>
)
}Styling
Passing Tailwind CSS classes
import { Tabs } from "heroui-solid";
function CustomTabs() {
return (
<Tabs class="w-full max-w-lg text-center">
<Tabs.ListContainer>
<Tabs.List
aria-label="Options"
class="*:data-[selected=true]:text-accent-foreground w-fit *:h-6 *:w-fit *:px-3 *:text-sm *:font-normal"
>
<Tabs.Tab id="daily">Daily<Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab id="weekly">Weekly<Tabs.Indicator /></Tabs.Tab>
</Tabs.List>
</Tabs.ListContainer>
</Tabs>
);
}CSS Classes
The Tabs component uses these CSS classes:
Base Classes
.tabs- Base tabs container.tabs__list-container- Tab list container wrapper.tabs__list-container__scroller- Scrollable wrapper around the tab list (handles overflow and fading edges).tabs__list-container__scroll-prev- Scroll chevron button for the previous (left / up) direction.tabs__list-container__scroll-next- Scroll chevron button for the next (right / down) direction.tabs__list- Tab list container.tabs__tab- Individual tab button.tabs__separator- Separator between tabs.tabs__panel- Tab panel content.tabs__indicator- Tab indicator
Orientation Attributes
.tabs[data-orientation="horizontal"]- Horizontal tab layout (default).tabs[data-orientation="vertical"]- Vertical tab layout
Variant Classes
.tabs--secondary- Secondary variant with underline indicator
Interactive States
The component supports both CSS pseudo-classes and data attributes:
- Selected:
[aria-selected="true"]or[data-selected="true"] - Hover:
:hoveror[data-hovered="true"] - Focus:
:focus-visible - Disabled:
[aria-disabled="true"]or[data-disabled="true"]
API Reference
Tabs Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "primary" | "secondary" | "primary" | Visual style variant. Primary uses a filled indicator, secondary uses an underline indicator |
orientation | "horizontal" | "vertical" | "horizontal" | Tab layout orientation |
selectedKey | string | - | Controlled selected tab key |
defaultSelectedKey | string | - | Default selected tab key |
onSelectionChange | (key: string) => void | - | Selection change handler |
class | string | - | Additional CSS classes |
as | ValidComponent | "div" | Overrides the default DOM element (Kobalte polymorphic as) |
Tabs.ListContainer Props
| Prop | Type | Default | Description |
|---|---|---|---|
initialShadow | boolean | false | Render the end-edge fade and scroll chevron before scroll measurement (SSR / first frame). Set when the list is known to overflow, so they don't blink in after hydration. |
hideScrollButtons | boolean | false | Hide the scroll chevron buttons when the list overflows (the fading edges remain). |
class | string | - | Additional CSS classes |
Tabs.List Props
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | - | Accessibility label for tab list |
class | string | - | Additional CSS classes |
Tabs.Tab Props
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | - | Unique tab identifier |
isDisabled | boolean | false | Whether tab is disabled |
class | string | - | Additional CSS classes |
as | ValidComponent | "button" | Overrides the default DOM element (Kobalte polymorphic as) |
Tabs.Separator Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | string | - | Additional CSS classes |
Tabs.Panel Props
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | - | Panel identifier matching tab id |
class | string | - | Additional CSS classes |
as | ValidComponent | "div" | Overrides the default DOM element (Kobalte polymorphic as) |
Differences from HeroUI React
- Use
onClick(Solid's native event) instead of React Aria'sonPress. - Polymorphism uses Kobalte's
asprop instead of React'srenderprop override. onSelectionChangereports the key as a string.- Upstream renders a
SelectionIndicatorinside eachTabs.Tab; this port keeps that authoring shape (Kobalte has no selection-indicator primitive) and ports React Aria's shared-element transition so the indicator animates between tabs. Tabs.ListContaineracceptsinitialShadowto server-render the overflow chevron and fade for lists known to overflow, andhideScrollButtonsto omit the chevron buttons entirely.- The selected tab is kept in view: scrolled to instantly on mount, with an animated scroll on selection change.
Last updated: 7/19/26, 3:27 AM