Skip to content

NbTabs switches between sections of the same page. Give it an items array and bind v-model to the id of the section on screen. It renders the tab bar and, when you fill the matching slot, the panel below it.

Reach for tabs when the sections are peers and only one is relevant at a time. Do not use them for steps in a sequence, or to navigate to a different page.

vue
<template>
  <NbTabs v-model="activeTab" :items="items" aria-label="Client">
    <template #settings>
      <ClientSettings />
    </template>
    <template #hours>
      <ClientHours />
    </template>
    <template #invoices>
      <ClientInvoices />
    </template>
  </NbTabs>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import type { ITabItem } from '@nubisco/ui'

const items: ITabItem[] = [
  { id: 'settings', label: 'Settings' },
  { id: 'hours', label: 'Hours' },
  { id: 'invoices', label: 'Invoices' },
]
const activeTab = ref('settings')
</script>

Panels are optional ​

Leave the slots out and only the bar renders, which is what you want when the sections live in a router view or in separate components further down the page. The tab then carries no aria-controls, since there is no panel to point at.

vue
<template>
  <NbTabs v-model="section" :items="items" aria-label="Project" />
  <RouterView />
</template>

Variants ​

line is the default: text tabs on a rule, the active one underlined in the primary color. It is the treatment for a page's own sections.

contained is a segmented control with a filled active block. Use it for a mode switch that changes what a single surface does, such as sign in against sign up, not for page sections.

vue
<template>
  <NbTabs :items="authTabs" variant="contained" v-model="mode" />
</template>

Sizes ​

md is the baseline. Use sm on dense surfaces such as inspectors and side panels, and lg when the tabs head a full page.

Icons and badges ​

An item can carry an icon before its label and a badge after it. Keep the badge to a count or a very short status: it competes with the label for room.

vue
<script setup lang="ts">
const items = [
  { id: 'files', label: 'Files', icon: 'folder' },
  { id: 'issues', label: 'Issues', icon: 'warning-circle', badge: 12 },
]
</script>

Full width ​

full-width shares the available width equally between the tabs. It suits a segmented control spanning a form, and a bar of two or three tabs. Avoid it with many tabs, where each one stretches far past its label.

Disabled ​

Disable a single item with disabled on the item, or the whole bar with the disabled prop. Disabled tabs are skipped by keyboard navigation, so arrow keys never land on a tab that cannot be opened.

Props ​

PropTypeDefaultDescription
itemsITabItem[]requiredTabs to render, in display order
modelValuestringundefinedId of the selected tab, falls back to the first enabled
variant'line' | 'contained''line'Underlined text tabs, or a filled segmented control
size'sm' | 'md' | 'lg''md'Control size
disabledbooleanfalseDisables every tab in the bar
fullWidthbooleanfalseStretches tabs to share the available width equally
ariaLabelstringundefinedAccessible name of the tab bar, nothing is rendered
idstringundefinedExplicit id, auto-generated when omitted

ITabItem ​

FieldTypeDescription
idstringStable id, used as the model value and slot name
labelstringVisible tab text
iconstringOptional icon name rendered before the label
badgestring | numberOptional count or short status after the label
disabledbooleanPrevents selection and dims the tab

Events ​

EventPayloadDescription
update:modelValueid: stringThe newly selected tab id
changeid: string, item: ITabItemThe newly selected tab and item

Neither event fires when the already active tab is clicked.

Slots ​

SlotPropsDescription
[item.id]item: ITabItemPanel for that tab, rendered only while it is active

Keyboard ​

KeyAction
TabMoves into the bar, landing on the active tab
← →Selects the previous or next tab, wrapping
HomeSelects the first tab
EndSelects the last tab

Arrow keys select as they move (automatic activation), which the WAI-ARIA tabs pattern recommends when showing a panel is cheap.

Accessibility ​

  • The bar is a role="tablist", each tab a role="tab", each rendered panel a role="tabpanel" labelled by its tab.
  • Only the active tab is in the tab order (roving tabindex), so the bar is a single stop rather than one per tab.
  • aria-controls is set only when a panel is actually rendered, so it never points at a missing element.
  • Name the bar with ariaLabel when a page carries more than one set of tabs.