Author each tab as a native <button slot="tab"> and each panel as an element with slot="panel", matched by value. Tabs supplies roles, type="button", roving tabindex and the aria-controls and aria-labelledby links; label names the tab list.
Tab labels may include tp-icon, tp-badge, or other noninteractive content. Do not nest interactive controls inside a tab. Use tp-button, tp-input, tp-card and other library components inside panels. Nested Tabs own only their direct members. dir="rtl" follows the surrounding direction and reverses horizontal navigation; vertical navigation remains Up/Down.
Native button values are strings. For object or numeric member values, use a non-native tab host with native-action="false" and assign its .value before connection. Values use identity equality. After changing non-reactive member properties such as .value, .keepMounted, .disabled or .nativeAction, call refresh(). String attributes are observed automatically.
Variants
Underline
Default density
Compact density
Restart from the initial state
Show the copyable source
Website redesign: 14 open tasks, due March 28.
Jonas Weber moved “Pricing page” to Review 2 hours ago.
Visibility, integrations and archive options.
Website redesign: 14 open tasks, due March 28.
Jonas Weber moved “Pricing page” to Review 2 hours ago.
Visibility, integrations and archive options.
<tp-tabs variant="underline" default-value="overview" label="Project" style="max-inline-size:28rem;margin-inline:auto">
<button slot="tab" value="overview">Overview</button>
<button slot="tab" value="activity">Activity</button>
<button slot="tab" value="settings">Settings</button>
<p slot="panel" value="overview">Website redesign: 14 open tasks, due March 28.</p>
<p slot="panel" value="activity">Jonas Weber moved “Pricing page” to Review 2 hours ago.</p>
<p slot="panel" value="settings">Visibility, integrations and archive options.</p>
</tp-tabs>
enclosed (the default) suits tabs that switch a contained surface; underline sits lighter on page-level navigation. The indicator glides to the selected tab in both.
Vertical
Vertical
Default density
Compact density
Restart from the initial state
Show the copyable source
By default activation is manual: arrow keys move focus and Enter or Space selects. Use automatic when panels are cheap to show, so selection follows focus. Arrows wrap at the ends unless the loopFocus property is set to false.
Tab enters the list at its roving entry; another Tab reaches the active panel and its content. Left/Right navigate horizontal lists, Up/Down vertical lists, and Home/End move to the ends. Wrong-axis and modified arrows do not change selection. External selection does not steal focus or replace the focused tab’s roving entry while focus is in the list. Tabs use aria-selected/aria-controls; panels use aria-labelledby and are sequentially focusable only while selected. Author IDs are preserved.
Responding to changes
Change event and setValue
Default density
Compact density
Restart from the initial state
Show the copyable source
Selection publishes a cancelable tp-value-change with the proposed value; call preventDefault() to keep the current tab, for example while a panel has unsaved edits. setValue() selects a tab from code through the same proposal and returns whether it was accepted. For controlled Tabs, set the value property from the start and write each accepted detail.value back to it. Equal selections are no-ops. Filter bubbled events by event.target when panels contain other controls.
Guarding unsaved edits
For controlled Tabs, set value from the start and accept proposals in onValueChange. Skipping the write-back, or calling preventDefault(), keeps the current tab. Edit the name below, then try to leave the tab. The profile panel is keep-mounted, so its draft survives while another tab is showing. Programmatic controlled assignments do not emit change proposals. A controlled missing value selects nothing; the first enabled tab remains the keyboard entry.
Controlled with a veto
Default density
Compact density
Restart from the initial state
Show the copyable source
ProfileShown on comments and assignments.
SaveDiscard
NotificationsEmail me when someone mentions me or assigns me a task.
ProfileShown on comments and assignments.
SaveDiscard
NotificationsEmail me when someone mentions me or assigns me a task.
Default density
Compact density
Restart from the initial state
Show the copyable source
Order #10482, placed March 3.
Delivered March 6 by UPS.
Returns open after delivery.
Order #10482, placed March 3.
Delivered March 6 by UPS.
Returns open after delivery.
<tp-tabs default-value="details" label="Order" style="max-inline-size:26rem;margin-inline:auto">
<button slot="tab" value="details">Details</button>
<button slot="tab" value="shipping">Shipping</button>
<button slot="tab" value="returns" aria-disabled="true">Returns</button>
<p slot="panel" value="details">Order #10482, placed March 3.</p>
<p slot="panel" value="shipping">Delivered March 6 by UPS.</p>
<p slot="panel" value="returns">Returns open after delivery.</p>
</tp-tabs>
aria-disabled="true" keeps a tab discoverable through arrow navigation; native disabled removes it from focus. Neither can be activated. Setting disabled on Tabs itself locks the whole list, for example while the content behind it is loading. Programmatic selection may select a disabled tab, but disabled tabs are never the sequential tab stop or user-activated.
An explicit disabled defaultValue is honored initially. If a selected tab is removed, renamed or subsequently disabled, uncontrolled selection falls forward to the nearest enabled successor, then backward to a predecessor, then to null.
Panel mounting
Inactive panels are detached from the DOM after their exit and restored on activation, so heavy content costs nothing while hidden. Add keep-mounted to a panel that must stay in the document, for example one holding form fields that still submit, and it is kept hidden and inert instead. Custom elements inside them receive disconnect/connect callbacks. The public panels getter includes retained and detached nodes. Keep application references to panels that need updating while detached; a host querySelector cannot find them. Remove a panel by calling remove() on that node, including while detached.
API Reference
Every element also accepts motion-policy.
Attributes
AttributePropertyTypeDefaultDescriptionvaluevalueunknown—The selected tab's value. Set before the first render, it makes selection controlled: write each accepted proposal back to it.default-valuedefaultValueunknownundefinedInitial selection when Tabs owns it; without one, the first enabled tab is selected.activationactivation'automatic' | 'manual''manual'manual selects on Enter or Space; automatic selects as focus moves, for panels that are cheap to show.activate-on-focusactivateOnFocusboolean—Shorthand for activation="automatic".loop-focusloopFocusbooleantrueWhether arrow keys wrap from the last tab to the first; set the property to false to stop at the ends.render-before-activationrenderBeforeActivationbooleanfalseShows the indicator before any tab is selected and measured.variantvariant'enclosed' | 'underline''enclosed'enclosed for tabs that switch a contained surface, underline for lighter page-level navigation.labellabelstring''Accessible name of the tab list.
Properties
PropertyTypeDefaultDescriptiononValueChange((event: TpValueChangeEvent<unknown>) => void) | undefined—Called with each selection proposal before tp-value-change is dispatched; cancel it to keep the current tab.activationDirectionTabsActivationDirection'none'Which way the last selection moved: left, right, up, down or none.panelsread-onlyreadonly HTMLElement[]—Every panel, including inactive ones detached from the document.
Events
EventTypeDescriptiontp-value-changeTpValueChangeEvent<unknown>Cancelable proposal of the selected tab's value, from a press, a key or setValue(); detail.metadata.activationDirection says which way the selection moved.
Methods
MethodReturnsDescriptionrefresh()voidReads the tabs and panels again after their value, keepMounted, disabled or nativeAction properties change from script.setValue(value)booleanProposes a selection from code, as a press would; returns whether it was accepted.
Slots
SlotDescriptiontabThe tabs, preferably native buttons, each with a unique value; nothing interactive inside them.panelThe panels, each with the value of its tab; keep-mounted keeps an inactive one in the document, hidden and inert.indicatorA decorative element that replaces the default selection indicator.
CSS parts
PartDescription::part(tabs)The root around the tab list and the panels.::part(tabs-indicator)The default indicator that glides to the selected tab.::part(tabs-list)The tab list, which scrolls when the tabs overflow.
Related
Components, widgets and patterns that use this one.