Navigation
Tabs
One visible panel from a related set, chosen from a tab list in the same view.
Import
import { Tabs, TabsList, TabsTab, TabsPanel } from "@loamui/core";Usage
Every example has a CSStab. That’s the real, complete stylesheet for the component: plain, static CSS, with nothing running in the browser.
Basic
Uncontrolled via defaultValue. Arrow keys move between tabs.
With icons (composed as children)
No leftSection prop: an svg child is detected via :has(svg) and gets a gap and label-relative sizing, the same detection Button uses. Compose the icon before the label and mark it aria-hidden.
Disabled tab
A disabled tab is skipped by keyboard navigation.
Guidance
When to use it
- For parallel views of the same thing: Account / Security / Notifications are alternative facets of one settings object, and users need only one at a time.
- To keep related panels in one place without a page navigation, when switching views must not lose the surrounding context.
When not to
- On narrow screens where the tab strip no longer fits: stack the content under plain headings instead. A horizontally scrolling tab strip hides panels behind an interaction most users never find.
- For steps in a sequence: tabs imply no order and let users jump anywhere, so a flow with dependencies belongs on separate pages with visible progress.
- As primary navigation: switching a tab changes no URL and creates no history entry, so tabbed 'pages' can't be linked, bookmarked or reached with the back button.
- When users need to read or compare everything: content in an unselected tab may never be seen; stack it on the page under headings instead.
Accessibility
- The tab list uses a roving tabindex: only the active tab sits in the Tab order (tabIndex 0, the rest -1), so keyboard users cross the whole list in one Tab press instead of stepping through every tab.
- Arrow Left/Right move through the horizontal tabs and wrap at the ends, following the page direction in right-to-left content. Home/End jump to the first and last, and disabled tabs are skipped. Moving focus also selects, so no separate Enter press is needed.
- Inactive panels are hidden with hidden="until-found" where the browser supports it, so find-in-page can match text inside a closed tab; a beforematch event then activates that tab. Browsers without support fall back to plain hidden.
- The wiring is generated from one id: role="tablist"/"tab"/"tabpanel" with aria-selected, aria-controls on each tab and aria-labelledby on each panel, so assistive technology announces which tab is active and what it controls.
- Each panel has tabIndex 0, so a panel whose content contains no focusable element can still be reached and scrolled by keyboard.
How it works
Parallel views, not steps
Tabs present alternative views of one subject; the order of the tab list carries no meaning and users can activate any tab at any moment. If the content is a sequence, where step two only makes sense after step one, tabs actively work against you, because they advertise that jumping ahead is fine. Use separate pages and show progress instead.
View state stays out of the URL
Switching a tab updates React state, not the URL; reloading returns to defaultValue and the back button ignores tab changes. When a view should be linkable, use the controlled form (value/onChange) and mirror the value in the query string yourself; if every view deserves its own URL, you want pages with links, not tabs.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultValue | string | — | Required initial tab value for uncontrolled usage. Omit only when value is supplied. |
value | string | — | Controlled active tab value. Required when defaultValue is omitted. |
onChange | (value: string) => void | — | Called with the new value when the active tab changes. |
...others | HTMLAttributes<HTMLDivElement> | — | All native <div> props are forwarded. |
Parts
TabsList
The tab strip (role="tablist") that owns the roving tabindex and arrow-key behaviour; all native <div> props are forwarded.
TabsTab
One tab button; all native <button> props are forwarded.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Unique value linking this tab to its panel (required). |
disabled | boolean | — | Disable the tab and skip it in keyboard navigation. |
TabsPanel
The content shown while its tab is active; all native <div> props are forwarded.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Value of the tab this panel belongs to (required). |