Skip to content

Early previewhamur is not on npm yet. The components are being built in the open, follow along on GitHub.

LayoutReact

Tabs

A component for toggling between related panels on the same page.

Rendered by React 19.3.1

Loading React…
import { Tabs } from '@hamur/react/tabs'

export default function TabsExample() {
  return (
    <Tabs.Root className="tabs" defaultValue="overview">
      <Tabs.List className="tabs-list" aria-label="Workspace">
        <Tabs.Tab className="tab" value="overview">
          Overview
        </Tabs.Tab>
        <Tabs.Tab className="tab" value="projects">
          Projects
        </Tabs.Tab>
        <Tabs.Tab className="tab" value="billing" disabled>
          Billing
        </Tabs.Tab>
        <Tabs.Tab className="tab" value="account">
          Account
        </Tabs.Tab>
        <Tabs.Indicator className="tabs-indicator" />
      </Tabs.List>
      <Tabs.Panel className="tabs-panel" value="overview">
        Workspace stats and activity.
      </Tabs.Panel>
      <Tabs.Panel className="tabs-panel" value="projects">
        Milestones and deadlines.
      </Tabs.Panel>
      <Tabs.Panel className="tabs-panel" value="billing">
        Invoices and plans.
      </Tabs.Panel>
      <Tabs.Panel className="tabs-panel" value="account">
        Profile and preferences.
      </Tabs.Panel>
    </Tabs.Root>
  )
}
457Tests passing in React · 0 failing
16.0 KiBSize
Not measured yetMemory
Not measured yetSpeed
productdevbook and 15 upstream authors10 commits · last

Installation

npm i @hamur/react

Not on npm yet. The package goes out under the chosen name.

Anatomy

Import the component and assemble its parts:

<Tabs.Root>
  <Tabs.List>
    <Tabs.Tab />
    <Tabs.Indicator />
  </Tabs.List>
  <Tabs.Panel />
</Tabs.Root>

Examples

Animated Panels

Panels slide in from the side given by data-activation-direction.

TabsAnimatedPanels.tsx
import { Tabs } from '@hamur/react/tabs'

export default function TabsAnimatedPanelsExample() {
  return (
    <Tabs.Root className="tabs" defaultValue="overview">
      <Tabs.List className="tabs-list">
        <Tabs.Tab className="tab" value="overview">
          Overview
        </Tabs.Tab>
        <Tabs.Tab className="tab" value="projects">
          Projects
        </Tabs.Tab>
        <Tabs.Tab className="tab" value="account">
          Account
        </Tabs.Tab>
        <Tabs.Indicator className="tabs-indicator" />
      </Tabs.List>
      <div className="tabs-panel-viewport">
        <Tabs.Panel className="tabs-panel tabs-panel-slide" value="overview">
          <p>Workspace stats and activity.</p>
        </Tabs.Panel>
        <Tabs.Panel className="tabs-panel tabs-panel-slide" value="projects">
          <p>Milestones and deadlines.</p>
        </Tabs.Panel>
        <Tabs.Panel className="tabs-panel tabs-panel-slide" value="account">
          <p>Profile and preferences.</p>
        </Tabs.Panel>
      </div>
    </Tabs.Root>
  )
}

API reference

12 props across 5 parts, in React syntax. className, style, render are listed once, under Shared props.

IndicatorTabs.Indicator

A visual indicator that can be styled to match the position of the currently active tab. Renders a <span>.

Indicator props
PropTypeDefault
renderBeforeHydrationWhether to render itself before React hydrates.booleanfalse
Indicator data attributes
Data attributeDescription
data-activation-directionIndicates the direction of the activation (based on the previous active tab).
data-orientationIndicates the orientation of the tabs.
Indicator CSS variables
CSS variableDescription
--active-tab-leftIndicates the distance on the left side from the parent's container if the tab is active.
--active-tab-rightIndicates the distance on the right side from the parent's container if the tab is active.
--active-tab-topIndicates the distance on the top side from the parent's container if the tab is active.
--active-tab-bottomIndicates the distance on the bottom side from the parent's container if the tab is active.
--active-tab-widthIndicates the width of the tab if it is active.
--active-tab-heightIndicates the height of the tab if it is active.

ListTabs.List

Groups the individual tab buttons. Renders a <div>.

List props
PropTypeDefault
activateOnFocusWhether to automatically change the active tab on arrow key focus.booleanfalse
loopFocusWhether to loop keyboard focus back to the first itembooleantrue
List data attributes
Data attributeDescription
data-activation-directionIndicates the direction of the activation (based on the previous active tab).
data-orientationIndicates the orientation of the tabs.

PanelTabs.Panel

A panel displayed when the corresponding tab is active. Renders a <div>.

Panel props
PropTypeDefault
valuerequiredThe value of the TabPanel.any—
keepMountedWhether to keep the HTML element in the DOM while the panel is hidden.booleanfalse
Panel data attributes
Data attributeDescription
data-indexIndicates the index of the tab panel.
data-activation-directionIndicates the direction of the activation (based on the previous active tab).
data-orientationIndicates the orientation of the tabs.
data-hiddenPresent when the panel is hidden.
data-starting-stylePresent when the panel begins animating in.
data-ending-stylePresent when the panel is animating out.

RootTabs.Root

Groups the tabs and the corresponding panels. Renders a <div>.

Root props
PropTypeDefault
defaultValueThe default value.any0
valueThe value of the currently active Tab.any—
onValueChangeCallback invoked when new value is being set.((value: any, eventDetails: TabsRootChangeEventDetails) => void)—
orientationThe component orientation (layout flow direction).Orientation'horizontal'
Root data attributes
Data attributeDescription
data-activation-directionIndicates the direction of the activation (based on the previous active tab).
data-orientationIndicates the orientation of the tabs.

TabTabs.Tab

An individual interactive tab button that toggles the corresponding panel. Renders a <button>.

Tab props
PropTypeDefault
valuerequiredThe value of the Tab.any—
nativeButtonWhether the component renders a native <button> element when replacing itbooleantrue
disabledWhether the Tab is disabled.boolean—
Tab data attributes
Data attributeDescription
data-activation-directionIndicates the direction of the activation (based on the previous active tab).
data-orientationIndicates the orientation of the tabs.
data-disabledPresent when the tab is disabled.
data-activePresent when the tab is active.

Shared props

Every part that renders an element takes these.

Shared props
PropType
classNameCSS class applied to the element, or a function that returns a class based on the component's state.string | ((state: State) => string | undefined)
styleStyle applied to the element, or a function that returns a style object based on the component's state.React.CSSProperties | ((state: State) => React.CSSProperties | undefined)
renderAllows you to replace the component's HTML element with a different tag, or compose it with another component.React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State>

Keyboard and accessibility

Keyboard

Keyboard
KeysDoesPart
ArrowRightArrowLeftMoves focus to the next or previous tab without activating it (ArrowDown and ArrowUp when vertical)List
ArrowRightArrowLeftWith activateOnFocus, moves focus and activates the tabList
ArrowRightFrom the last tab, wraps to the first while loopFocus is onList
HomeMoves focus to the first tabList
EndMoves focus to the last tabList
EnterSpaceActivates the focused tabTab

Accessibility

What the parts set
PartSetsSo that
Listrole="tablist", aria-orientation="vertical" when verticala screen reader announces a set of tabs and which arrow keys move in it
Tabrole="tab", aria-selected, aria-controls to its paneleach tab is read with its state and tied to its panel
Panelrole="tabpanel", aria-labelledby to its tabthe panel is read with the tab's name
Paneltabindex="0" while activekeyboard users can reach a panel that has no focusable content
Indicatorrole="presentation"the moving highlight is not read

Focus order

  1. The list is one tab stop: the active tab.
  2. Arrow keys, Home and End move between tabs; Enter or Space activates one unless activateOnFocus is on.
  3. The active Panel has tabindex="0", so the next Tab reaches it when it has no focusable content.