Tabs

Organizes content into distinct sections, allowing users to switch between them.

Prague

06:05

3h 30m

Malaga

06:05


	<script lang="ts">
  import { Tabs } from "bits-ui";
  import Airplane from "phosphor-svelte/lib/Airplane";
</script>
 
<div class="pt-6">
  <Tabs.Root
    value="outbound"
    class="w-[390px] rounded-card border border-muted bg-background-alt p-3 shadow-card"
  >
    <Tabs.List
      class="grid w-full grid-cols-2 gap-1 rounded-9px bg-dark-10 p-1 text-sm font-semibold leading-[0.01em] shadow-mini-inset dark:border dark:border-neutral-600/30 dark:bg-background"
    >
      <Tabs.Trigger
        value="outbound"
        class="h-8 rounded-[7px] bg-transparent py-2 data-[state=active]:bg-white data-[state=active]:shadow-mini dark:data-[state=active]:bg-muted"
        >Outbound</Tabs.Trigger
      >
      <Tabs.Trigger
        value="inbound"
        class="h-8 rounded-[7px] bg-transparent py-2 data-[state=active]:bg-white data-[state=active]:shadow-mini dark:data-[state=active]:bg-muted"
        >Inbound</Tabs.Trigger
      >
    </Tabs.List>
    <Tabs.Content value="outbound" class="pt-3">
      <div class="grid grid-cols-3 grid-rows-2 gap-0 p-4 pb-1">
        <div class="text-left">
          <h4
            class="mb-2 text-[20px] font-semibold leading-none tracking-[-0.01em]"
          >
            Prague
          </h4>
          <p class="text-sm font-medium text-muted-foreground">06:05</p>
        </div>
        <div class="self-end text-center">
          <p class="text-sm font-medium text-muted-foreground">3h 30m</p>
        </div>
        <div class="text-right">
          <h4
            class="mb-2 text-[20px] font-semibold leading-none tracking-[-0.01em]"
          >
            Malaga
          </h4>
          <p class="text-sm font-medium text-muted-foreground">06:05</p>
        </div>
        <div class="relative col-span-3">
          <hr
            class="border-1 relative top-4 h-px border-dashed border-border-input"
          />
 
          <div class="absolute left-1/2 -translate-x-1/2 bg-background-alt p-1">
            <Airplane class="size-6 rotate-90 text-muted-foreground" />
          </div>
        </div>
      </div>
    </Tabs.Content>
    <Tabs.Content value="inbound" class="pt-3">
      <div class="grid grid-cols-3 grid-rows-2 gap-0 p-4 pb-1">
        <div class="text-left">
          <h4
            class="mb-2 text-[20px] font-semibold leading-none tracking-[-0.01em]"
          >
            Malaga
          </h4>
          <p class="text-sm font-medium text-muted-foreground">07:25</p>
        </div>
        <div class="self-end text-center">
          <p class="text-sm font-medium text-muted-foreground">3h 20m</p>
        </div>
        <div class="text-right">
          <h4
            class="mb-2 text-[20px] font-semibold leading-none tracking-[-0.01em]"
          >
            Prague
          </h4>
          <p class="text-sm font-medium text-muted-foreground">10:45</p>
        </div>
        <div class="relative col-span-3">
          <hr
            class="border-1 relative top-4 h-px border-dashed border-border-input"
          />
 
          <div class="absolute left-1/2 -translate-x-1/2 bg-background-alt p-1">
            <Airplane class="size-6 rotate-90 text-muted-foreground" />
          </div>
        </div>
      </div>
    </Tabs.Content>
  </Tabs.Root>
</div>

Structure

	<script lang="ts">
	import { Tabs } from "bits-ui";
</script>
 
<Tabs.Root>
	<Tabs.List>
		<Tabs.Trigger />
	</Tabs.List>
	<Tabs.Content />
</Tabs.Root>

Value State

The value represents the currently selected tab within the Tabs component.

Bits UI provides flexible options for controlling and synchronizing the Tabs component's value state.

Two-Way Binding

Use the bind:value directive for effortless two-way synchronization between your local state and the Tabs component's value.

	<script lang="ts">
	import { Tabs } from "bits-ui";
	let value = $state("");
</script>
 
<button onclick={() => (value = "A")}> Set to A </button>
<Tabs.Root bind:value>
	<!-- ... -->
</Tabs.Root>

This setup enables changing the Tabs component's value via the custom button and ensures the local value state is synchronized with the Tabs component's value.

Change Handler

You can also use the onValueChange prop to update local state when the Tabs component's value changes. This is useful when you don't want two-way binding for one reason or another, or you want to perform additional logic when the Tabs component's value changes.

	<script lang="ts">
	import { Tabs } from "bits-ui";
	let value = $state("");
</script>
 
<Tabs.Root
	bind:value
	onValueChange={(v) => {
		value = v;
	}}
>
	<!-- ... -->
</Tabs.Root>

Controlled

Sometimes, you may want complete control over the component's value state, meaning you will be "kept in the loop" and be required to apply the state change yourself. While you will rarely need this, it's possible to do so by setting the controlledValue prop to true.

You will then be responsible for updating a local value state variable that is passed as the value prop to the Tabs.Root component.

	<script lang="ts">
	import { Tabs } from "bits-ui";
 
	let myValue = $state();
</script>
 
<Tabs.Root controlledValue value={myValue} onValueChange={(v) => (myValue = v)}>
	<!-- ... -->
</Tabs.Root>

See the Controlled State documentation for more information about controlled states.

Orientation

The orientation prop is used to determine the orientation of the Tabs component, which influences how keyboard navigation will work.

When the orientation is set to 'horizontal', the ArrowLeft and ArrowRight keys will move the focus to the previous and next tab, respectively. When the orientation is set to 'vertical', the ArrowUp and ArrowDown keys will move the focus to the previous and next tab, respectively.

	<Tabs.Root orientation="horizontal">
	<!-- ... -->
</Tabs.Root>
 
<Tabs.Root orientation="vertical">
	<!-- ... -->
</Tabs.Root>

API Reference

Tabs.Root

The root tabs component which contains the other tab components.

Property Type Description
value $bindable
string

The active tab value.

Default: undefined
onValueChange
function

A callback function called when the active tab value changes.

Default: undefined
controlledValue
boolean

Whether or not the value is controlled or not. If true, the component will not update the value state internally, instead it will call onValueChange when it would have otherwise, and it is up to you to update the value prop that is passed to the component.

Default: false
activationMode
enum

How the activation of tabs should be handled. If set to 'automatic', the tab will be activated when the trigger is focused. If set to 'manual', the tab will be activated when the trigger is pressed.

Default: 'automatic'
disabled
boolean

Whether or not the tabs are disabled.

Default: false
loop
boolean

Whether or not the tabs should loop when navigating with the keyboard.

Default: true
orientation
enum

The orientation of the tabs.

Default: horizontal
ref $bindable
HTMLDivElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
child
Snippet

Use render delegation to render your own element. See delegation docs for more information.

Default: undefined
Data Attribute Value Description
data-orientation
enum

The orientation of the tabs.

data-tabs-root
''

Present on the root element.

Tabs.List

The component containing the tab triggers.

Property Type Description
ref $bindable
HTMLDivElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
child
Snippet

Use render delegation to render your own element. See delegation docs for more information.

Default: undefined
Data Attribute Value Description
data-orientation
enum

The orientation of the tabs.

data-tabs-list
''

Present on the list element.

Tabs.Trigger

The trigger for a tab.

Property Type Description
value required
string

The value of the tab this trigger represents.

Default: undefined
disabled
boolean

Whether or not the tab is disabled.

Default: false
ref $bindable
HTMLButtonElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
child
Snippet

Use render delegation to render your own element. See delegation docs for more information.

Default: undefined
Data Attribute Value Description
data-state
enum

The state of the tab trigger.

data-value
''

The value of the tab this trigger represents.

data-orientation
enum

The orientation of the tabs.

data-disabled
''

Present when the tab trigger is disabled.

data-tabs-trigger
''

Present on the trigger elements.

Tabs.Content

The panel containing the contents of a tab.

Property Type Description
value required
string

The value of the tab this content represents.

Default: undefined
ref $bindable
HTMLDivElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
child
Snippet

Use render delegation to render your own element. See delegation docs for more information.

Default: undefined
Data Attribute Value Description
data-tabs-content
''

Present on the content elements.