Naive UI Tabs Component
Switch contents in same area. Organize content into separate tabbed panels.
When to Use
Use this component when:
- Content organization: Group related content into tabs
- Form sections: Separate form fields into logical groups
- Settings panels: Organize settings into categories
- Dashboard views: Switch between different data views
When to Invoke
Invoke this skill when:
- User needs to implement tabbed interfaces
- User wants to switch between content panels
- User needs addable or closable tabs
- User wants to customize tab appearance
- User asks about tab navigation or switching
Features
- Multiple Types: Bar, line, card, and segment types
- Addable/Closable: Add and remove tabs dynamically
- Animated Transitions: Smooth tab switching animations
- Placement Options: Top, bottom, left, right placement
- Trigger Modes: Click or hover to activate
- Before Leave Hook: Intercept tab switching
- Display Directives: Control content rendering behavior
API Reference
Tabs Props
| Name | Type | Default | Description | Version |
|---|---|---|---|---|
| addable | boolean | object |
false |
Whether to allow adding tabs (card type only). | |
| add-tab-class | string |
undefined |
Class of the add tab. | 2.37.0 |
| add-tab-style | string | object |
undefined |
Style of the add tab. | 2.37.0 |
| animated | boolean |
false |
Whether to enable animation. | 2.27.0 |
| bar-width | number |
undefined |
Width of the tab bar. | 2.25.0 |
| closable | boolean |
false |
Whether to allow closing tabs (card type only). | |
| default-value | string | number |
undefined |
Default value in uncontrolled mode. | |
| justify-content | string |
undefined |
Flex justify-content value. | 2.29.1 |
| size | 'small' | 'medium' | 'large' |
'medium' |
Size of tabs. | |
| pane-class | string |
undefined |
Class of the pane. | |
| pane-style | string | object |
undefined |
Style of the pane. | |
| pane-wrapper-class | string |
undefined |
Class of the pane wrapper. | 2.34.4 |
| pane-wrapper-style | string | object |
undefined |
Style of the pane wrapper. | 2.34.4 |
| placement | 'left' | 'right' | 'top' | 'bottom' |
'top' |
Placement of tabs. | 2.34.4 |
| tab-class | string |
undefined |
Class of the tab. | 2.37.0 |
| tab-style | string | object |
undefined |
Style of the tab. | |
| tabs-padding | number |
0 |
Left & right padding of tabs. | |
| trigger | 'click' | 'hover' |
'click' |
Trigger of activating a tab. | 2.27.0 |
| type | 'bar' | 'line' | 'card' | 'segment' |
'bar' |
Tabs type. | |
| value | string | number |
undefined |
Value in controlled mode. | |
| on-add | () => void |
undefined |
Callback when adding tab. | |
| on-before-leave | (name, oldName) => boolean | Promise |
undefined |
Hook before switching tabs. | |
| on-close | (name) => void |
undefined |
Callback when closing tab. | |
| on-update:value | (value) => void |
undefined |
Callback when value changes. |
TabPane Props
| Name | Type | Default | Description | Version |
|---|---|---|---|---|
| closable | boolean |
false |
Whether to allow closing (card type only). | |
| disabled | boolean |
false |
Whether to disable the tab. | |
| display-directive | 'if' | 'show' | 'show:lazy' |
'if' |
Conditional rendering directive. | |
| name | string | number |
undefined |
Required, the name of the tab. | |
| tab | string | VNode | (() => VNodeChild) |
undefined |
Tab label. | |
| tab-props | Object |
undefined |
DOM attributes of tab label. | 2.24.2 |
Tab Props
| Name | Type | Default | Description |
|---|---|---|---|
| closable | boolean |
false |
Whether to allow closing (card type only). |
| disabled | boolean |
false |
Whether to disable. |
| name | string | number |
undefined |
Required, the name of the tab. |
Tabs Slots
| Name | Parameters | Description |
|---|---|---|
| default | () |
Tabs content. |
| prefix | () |
Tabs prefix. |
| suffix | () |
Tabs suffix. |
TabPane Slots
| Name | Parameters | Description |
|---|---|---|
| default | () |
Tab pane content. |
| tab | () |
Tab label content. |
Tabs Methods
| Name | Type | Description | Version |
|---|---|---|---|
| syncBarPosition | () => void |
Sync tab bar's position. | 2.24.0 |
Basic Usage
Basic Tabs
<template>
<n-tabs type="line" animated>
<n-tab-pane name="oasis" tab="Oasis">
Wonderwall
</n-tab-pane>
<n-tab-pane name="the beatles" tab="the Beatles">
Hey Jude
</n-tab-pane>
<n-tab-pane name="jay chou" tab="Jay Chou">
Qilixiang
</n-tab-pane>
</n-tabs>
</template>
Segment Type
<template>
<n-tabs type="segment" animated>
<n-tab-pane name="oasis" tab="Oasis">
Wonderwall
</n-tab-pane>
<n-tab-pane name="the beatles" tab="the Beatles">
Hey Jude
</n-tab-pane>
<n-tab-pane name="jay chou" tab="Jay Chou">
Qilixiang
</n-tab-pane>
</n-tabs>
</template>
Card Type with Closable
<template>
<n-tabs
v-model:value="name"
type="card"
closable
tab-style="min-width: 80px;"
@close="handleClose"
>
<n-tab-pane
v-for="panel in panels"
:key="panel"
:tab="panel.toString()"
:name="panel"
>
{{ panel }}
</n-tab-pane>
</n-tabs>
</template>
<script setup>
import { ref } from 'vue'
const name = ref(1)
const panels = ref([1, 2, 3, 4])
const handleClose = (name) => {
const index = panels.value.indexOf(name)
if (index > -1) {
panels.value.splice(index, 1)
}
}
</script>
Common Patterns
Addable Tabs
<template>
<n-tabs
v-model:value="value"
type="card"
:addable="addable"
:closable="closable"
tab-style="min-width: 80px;"
@close="handleClose"
@add="handleAdd"
>
<n-tab-pane v-for="panel in panels" :key="panel" :name="panel">
{{ panel }}
</n-tab-pane>
</n-tabs>
</template>
<script setup>
import { ref } from 'vue'
const value = ref(1)
const panels = ref([1, 2, 3])
const addable = ref(true)
const closable = ref(true)
const handleAdd = () => {
const newPanel = Math.max(...panels.value) + 1
panels.value.push(newPanel)
value.value = newPanel
}
const handleClose = (name) => {
const index = panels.value.indexOf(name)
if (index > -1) {
panels.value.splice(index, 1)
}
}
</script>
Before Leave Hook
<template>
<n-tabs
type="line"
default-value="okay"
@before-leave="handleBeforeLeave"
@update:value="handleUpdateValue"
>
<n-tab-pane name="wait" tab="Wait for 1s">
+1s
</n-tab-pane>
<n-tab-pane name="not-allowed" tab="Not allowed">
???
</n-tab-pane>
<n-tab-pane name="okay" tab="Okay">
Just so so
</n-tab-pane>
</n-tabs>
</template>
<script setup>
const handleBeforeLeave = async (activeName, oldActiveName) => {
if (activeName === 'not-allowed') {
return false
}
if (activeName === 'wait') {
await new Promise(resolve => setTimeout(resolve, 1000))
}
return true
}
const handleUpdateValue = (value) => {
console.log('Tab changed to:', value)
}
</script>
Prefix and Suffix
<template>
<n-tabs default-value="oasis">
<template #prefix>
Prefix
</template>
<n-tab-pane name="oasis" tab="Oasis">
Wonderwall
</n-tab-pane>
<n-tab-pane name="the beatles" tab="the Beatles">
Hey Jude
</n-tab-pane>
<template #suffix>
Suffix
</template>
</n-tabs>
</template>
Tabs Without Pane
<template>
<n-tabs type="line">
<n-tab name="Riders On the Storm">
Riders On the Storm
</n-tab>
<n-tab name="Light My Fire">
Light My Fire
</n-tab>
<n-tab name="Break On Through">
Break On Through
</n-tab>
</n-tabs>
</template>
Placement Options
<template>
<n-tabs
:type="type"
animated
:placement="placement"
:style="placement === 'left' || placement === 'right' ? { height: '240px' } : undefined"
>
<n-tab-pane name="oasis" tab="Oasis">
Wonderwall
</n-tab-pane>
<n-tab-pane name="the beatles" tab="the Beatles">
Hey Jude
</n-tab-pane>
<n-tab-pane name="jay chou" tab="Jay Chou">
Qilixiang
</n-tab-pane>
</n-tabs>
</template>
<script setup>
import { ref } from 'vue'
const type = ref('line')
const placement = ref('top')
</script>
Display Directive
<template>
<n-tabs default-value="show">
<n-tab-pane name="show" display-directive="show" tab="show">
Content preserved when switching
</n-tab-pane>
<n-tab-pane name="if" display-directive="if" tab="if">
Content reset when switching
</n-tab-pane>
<n-tab-pane name="show:lazy" display-directive="show:lazy" tab="show:lazy">
Lazy loaded and preserved
</n-tab-pane>
</n-tabs>
</template>
Hover Trigger
<template>
<n-tabs type="line" trigger="hover">
<n-tab-pane name="oasis" tab="Oasis">
Wonderwall
</n-tab-pane>
<n-tab-pane name="the beatles" tab="the Beatles">
Hey Jude
</n-tab-pane>
<n-tab-pane name="jay chou" tab="Jay Chou">
Qilixiang
</n-tab-pane>
</n-tabs>
</template>
Best Practices
Use appropriate type: Choose tab type based on context - card for editable, segment for toggle groups
Set default-value: Always set
default-valueto avoid Vue slot warningsUse display-directive wisely: Use
showto preserve state,iffor fresh contentImplement before-leave: Validate form data before allowing tab switch
Animated transitions: Enable
animatedfor better UXLimit tab count: Keep tabs manageable, use scrolling or dropdowns for many tabs