For a sequential process with steps and progress, use CdsSteps; to pick a single value in a form, use CdsButtonToggle.
Anatomy
Tabs consist of a bar and a content area: with tabs from items the content area is rendered automatically, with manual markup CdsTabsView is composed next to the bar. The bar is a container with tab buttons; the selected tab is highlighted by a moving slider.
Variations
Tabs and content
Sections are declared with CdsTab components in the default slot, and the content with CdsTabsViewItem components inside CdsTabsView. The value props bind a tab to its content, and a shared v-model keeps the bar and the content area in sync.
Tabs from an array
Pass an array of strings or { text, value } objects into items — the tabs and the content area are rendered automatically. The content of a section comes from the item.{value} slots, the label of a tab from the tab.{value} slots.
Alignment
The align prop pushes the tabs to the start of the bar (the default), to the center or to the end.
Layout
The grow prop stretches the tabs across the whole width of the bar — every tab takes an equal share of the free space.
The fixed-tabs prop also equalizes the width of the tabs, but caps it at 300px and centers the group.
The direction="vertical" prop turns the bar into a column — handy for settings and sections with a long list. The content area is placed next to it with a regular flex row.
Icons
A tab accepts the same content props as a button: prepend-icon and append-icon add an icon next to the text, icon leaves only the icon. For an icon tab do not forget aria-label — without text it stays the only name of the section.
The stacked prop puts the icon above the text — the tab becomes taller and more noticeable. Such a layout works well with align="center" in mobile scenarios.
States
The disabled prop turns a tab off, loading shows a loading indicator instead of the content.
Colors
Tabs accept the color aliases of the palette (neutral, red, green, cyan, purple…) or any CSS color. The color prop paints the text of the tabs, active-color the slider of the selected tab, bg-color the background of the whole bar, and base-color the background of the unselected tabs.
Scrolling
When the tabs do not fit into the bar, it scrolls. The show-arrows prop adds scroll arrows, center-active keeps the selected tab in the center of the bar.
Slider
The slider-transition prop picks the animation of the slider under the selected tab: shift (the default) moves it to the new tab, grow grows it in place, fade fades it in. The duration is set through slider-transition-duration, and hide-slider removes the slider entirely.
Slots
Slots replace the automatic rendering from items when you need your own markup:
| Slot | Description |
|---|---|
default | Your own CdsTab when items is not set |
tab, tab.{value} | Tab for an items element: shared and for a specific value |
item, item.{value} | Section content: shared and for a specific value |
view | Extra content inside CdsTabsView |
prev, next | Your own scroll arrows |
The tab and item slots receive the array element and replace the whole component — return CdsTab and CdsTabsViewItem with your own value from them. The tab.{value} and item.{value} slots replace the content only.
Guidelines
Scenarios
Card sections
Classic tabs: a product card with sections and a shared v-model. The review counter is a CdsBadge with the inline prop right inside the tab slot.
List filter
Tabs work without a content area too: v-model holds the selected status and the list is filtered by it through a computed. There is a single content area — only the selection changes.
Lazy loading of sections
A heavy section loads on the first open: a watch on v-model fires the request, CdsSkeletonLoader stands in for the content while it loads, and the already loaded sections open instantly.
Sections in a "More" menu
When there are more sections than fit into the bar, the rare ones hide into a CdsMenu whose activator is a regular "More" tab in the same bar. A click on "More" immediately shows the first section from the menu, the selected item takes the place of the first visible tab (the displaced one moves into the menu), and if the menu is closed without a choice, the bar and the content return to the current section.
API
CdsTabs
Props
| Name | Type | Default | Description |
|---|---|---|---|
activeColor | string | undefined | — | Color of the component in its active state |
align | "title" | "start" | "end" | "center" | undefined | 'start' | Alignment of the tabs inside the bar |
baseColor | string | undefined | — | Color of the component in its inactive state |
bgColor | string | undefined | — | Background color of the component |
centerActive | boolean | undefined | — | Forces the active component to be aligned to the center. |
color | string | undefined | — | Component color |
contentClass | any | — | Adds classes to the slide group container |
contentStyle | any | — | Adds inline styles to the slide group container |
direction | "horizontal" | "vertical" | undefined | 'horizontal' | Switches the component between horizontal and vertical layout. |
disabled | boolean | null | undefined | null | Disabled state |
fixedTabs | boolean | undefined | — | Makes all tabs the same width regardless of the length of their labels |
grow | boolean | undefined | — | Stretches the tabs across the whole available width of the container |
height | string | number | undefined | — | Height of the tab bar. A number is in pixels, a string takes any CSS unit |
hideSlider | boolean | undefined | — | Hides the underline indicator of the active tab |
inset | boolean | undefined | — | Styling with a background: the active tab is highlighted with a filled pill instead of an underline |
insetPadding | string | number | undefined | — | Padding of the background in inset mode. A number is in pixels, a string takes any CSS unit |
insetRadius | string | number | undefined | — | Corner radius of the background in inset mode. A number is in pixels, a string takes any CSS unit |
items | readonly TabItem[] | undefined | [] | List of tabs. A string or a number is a label, an object is a tab with arbitrary CdsTab props |
mandatory | boolean | "force" | undefined | 'force' | Requires at least one item to stay selected |
max | number | undefined | — | Maximum number of items that can be selected |
mobile | boolean | null | undefined | null | Determines the display mode of the component: true renders it in mobile mode, false in desktop mode, null makes the mode depend on the current breakpoint. |
mobileBreakpoint | number | "xs" | "sm" | "md" | "lg" | "xl" | undefined | — | Sets the breakpoint at which the component switches to mobile mode. Accepts a number (for example, 600) or the name of a predefined breakpoint (xs, sm, md, lg, xl). |
modelValue | any | — | Value of the selected items of the group |
multiple | boolean | undefined | — | Allows selecting several items at once |
nextIcon | IconValue | undefined | 'chevron-right' | Icon for the “next” button. |
prevIcon | IconValue | undefined | 'chevron-left' | Icon for the “previous” button. |
required | boolean | "force" | undefined | 'force' | Requires at least one item to stay selected |
scrollToActive | boolean | undefined | true | When true, the container scrolls the active component into view whenever it changes. |
selectedClass | string | undefined | 'cds-tabs--selected' | CSS class applied in the selected state |
showArrows | string | boolean | undefined | — | Changes how the arrows are shown. By default the arrows appear on desktop devices when the container overflows and stay hidden on mobile devices. true also shows them on mobile on overflow, desktop always shows them on desktop, mobile always on mobile, always on both, and never disables the arrows. |
sliderTransition | "shift" | "grow" | "fade" | undefined | — | Animation of the underline indicator of the active tab |
sliderTransitionDuration | string | number | undefined | — | Duration of the indicator animation in milliseconds. 0 disables the flight; by default it follows the motion scale |
spaced | "both" | "start" | "end" | undefined | — | Pushes the prepend and append slots apart to the edges of the button. |
stacked | boolean | undefined | — | Lays out the icon and the label of the tab vertically — the icon above the text |
tag | string | JSXComponent<any> | undefined | 'div' | Custom HTML tag or component to render |
textColor | string | undefined | — | Text color of the component |
Events
| Name | Type | Description |
|---|---|---|
update:modelValue | [value: unknown] | Fires when the tab is switched. Passes the value of the new active tab |
Slots
| Name | Type | Description |
|---|---|---|
default | any | Tabs and content when they are declared by hand instead of the items prop |
item | CdsTabsSlot<any> | Template for the tab content. The slot props carry the element from items |
next | any | Button that scrolls the tab bar forward |
prev | any | Button that scrolls the tab bar backward |
tab | CdsTabsSlot<any> | Template for the tab label. The slot props carry the element from items |
view | any | The whole tab content area — replaces the automatic CdsTabsView |
CdsTab
Props
| Name | Type | Default | Description |
|---|---|---|---|
activeColor | string | undefined | — | Color of the component in its active state |
appearancedeprecated | "inverse" | "primary" | "secondary" | "tertiary" | "transparent" | undefined | — | Visual appearance of the component. Deprecated: use variant and color |
appendIcon | IconValue | undefined | — | Icon in the append slot, after the content |
baseColor | string | undefined | — | Color of the component in its inactive state |
block | boolean | undefined | — | Stretches the button to the full width of its container |
color | (string & {}) | "neutral" | "brand" | "accent" | "danger" | "inverse" | undefined | 'brand' | Component color: a role, a palette alias, a theme token or a CSS color |
content | string | number | undefined | — | Content displayed in the component. |
direction | "horizontal" | "vertical" | undefined | 'horizontal' | Switches the component between horizontal and vertical layout. |
disabled | boolean | null | undefined | null | Disabled state |
exact | boolean | undefined | — | If true, the active state applies only on an exact route match |
fixed | boolean | undefined | — | Fixes the width of the tab so it does not stretch to the length of its label |
height | string | number | undefined | — | Component height |
hideSlider | boolean | undefined | — | Hides the underline indicator of the active tab |
href | string | undefined | — | The href attribute of the link. When set, the component behaves as a regular link |
icon | boolean | IconValue | undefined | — | Icon to display; the component renders as an icon-only button |
loading | string | boolean | undefined | — | Loading indicator for the component |
maxHeight | string | number | undefined | — | Maximum component height |
maxWidth | string | number | undefined | — | Maximum component width |
minHeight | string | number | undefined | — | Minimum component height |
minWidth | string | number | undefined | — | Minimum component width |
prependIcon | IconValue | undefined | — | Icon in the prepend slot, before the content |
readonly | boolean | null | undefined | null | Read-only state |
replace | boolean | undefined | — | If true, navigation replaces the current history entry instead of pushing a new one |
selectedClass | string | undefined | 'cds-tab--selected' | CSS class applied in the selected state |
size | number | "xs" | "sm" | "md" | "lg" | "xl" | undefined | 'md' | Size of the component |
sliderTransition | "shift" | "grow" | "fade" | undefined | — | Animation of the underline indicator of the active tab |
sliderTransitionDuration | string | number | undefined | — | Duration of the indicator animation in milliseconds. 0 disables the flight; by default it follows the motion scale |
spaced | "both" | "start" | "end" | undefined | — | Pushes the prepend and append slots apart to the edges of the button. |
stacked | boolean | undefined | — | Stacks the icon above the text instead of placing them side by side |
tag | string | JSXComponent<any> | undefined | 'button' | Custom HTML tag or component to render |
text | string | number | undefined | — | Text displayed in the component. |
theme | string | undefined | — | Component theme |
to | LinkTo | undefined | — | Target route or route location object for RouterLink. When set, the component behaves as a RouterLink |
value | any | — | Value of the component within the group |
variant | "solid" | "soft" | "outline" | "surface" | "ghost" | undefined | 'solid' | Component variant: solid, soft, outline, surface, ghost |
width | string | number | undefined | — | Component width |
Slots
| Name | Type | Description |
|---|---|---|
append | any | Content after the text |
default | any | Content inside the button |
loader | any | Loading indicator |
prepend | any | Content before the text |
CdsTabsView
Props
| Name | Type | Default | Description |
|---|---|---|---|
crossfade | boolean | undefined | — | Slides change with a smooth crossfade instead of a shift |
direction | "horizontal" | "vertical" | undefined | 'horizontal' | Switches the component between horizontal and vertical layout. |
disabled | boolean | null | undefined | null | Disabled state |
max | number | undefined | — | Maximum number of items that can be selected |
modelValue | any | — | Value of the selected items of the group |
multiple | boolean | undefined | — | Allows selecting several items at once |
reverse | boolean | undefined | — | Inverts the direction of the transition animation between slides |
selectedClass | string | undefined | — | CSS class applied in the selected state |
tag | string | JSXComponent<any> | undefined | 'div' | Custom HTML tag or component to render |
theme | string | undefined | — | Component theme |
transitionDuration | number | undefined | — | Duration of the transition animation between slides, in milliseconds |
verticalArrows | boolean | "left" | "right" | undefined | — | Places the navigation arrows vertically: 'left' or 'right' — the side, true — the default side |
Events
| Name | Type | Description |
|---|---|---|
update:modelValue | [value: any] | Fires when the shown content changes. Passes the value of the new active tab |
Slots
| Name | Type | Description |
|---|---|---|
additional | { group: GroupProvide; } | Extra content above the slides — indicator dots or your own navigation, for example |
default | { group: GroupProvide; } | Slides. The slot props carry the group state for controlling the active slide |
next | { props: ControlProps; } | Button that moves to the next slide. The slot props are ready to bind to your own button |
prev | { props: ControlProps; } | Button that moves to the previous slide. The slot props are ready to bind to your own button |
CdsTabsViewItem
Props
| Name | Type | Default | Description |
|---|---|---|---|
disabled | boolean | null | undefined | null | Disabled state |
eager | boolean | undefined | — | Renders the content of the component right away. Useful when you have content that is not in the DOM but has to be indexed for SEO. |
reverseTransition | string | boolean | undefined | — | Transition used when the slide appears in the reverse direction. A transition component name, or false to turn the animation off |
selectedClass | string | undefined | — | CSS class applied in the selected state |
transition | string | boolean | undefined | — | Transition used when the slide appears in the forward direction. A transition component name, or false to turn the animation off |
value | any | — | Value of the component within the group |
Slots
| Name | Type | Description |
|---|---|---|
default | any | Content of the slide |