Skip to main content

Tabs

This section describes how to implement custom tab renderers

Register a Tab Component

Default Tab Renderer

Accessing Custom Panel Parameters

You can provide a generic type that matches the structure of the expected custom panels parameters to provide type-hints for the panel parameters which can be accessed via the params option.

Extend the Default Tab Implementation

If you only want to make minor changes to the tab rendering you may be able to use the default implementation as a base. This could include:

  • Hiding the close button
  • Attaching additional event listeners

The example below shows a custom tab renderer that displays the panel title alongside a live-updating parameter value.

Tab Context Menu

Right-clicking a tab can show a context menu. This is opt-in — no menu is shown unless getTabContextMenuItems is provided. Return an empty array to suppress the menu for specific panels.

Built-in items

Pass string shortcuts to render standard menu entries without any extra code:

ValueBehaviour
'close'Close the right-clicked panel
'closeOthers'Close every other panel in the same group
'closeAll'Close all panels in the group
'separator'Render a visual divider

Custom label items

Provide an object with a label and action to add a simple clickable entry:

getTabContextMenuItems: (params) => [
{ label: 'Log panel id', action: () => console.log(params.panel.id) },
{ label: 'Disabled entry', action: () => {}, disabled: true },
]

Custom component items

For richer UI, provide a framework component via the component field. The component receives panel, group, api, close, and an optional componentProps object.

Full Width Tab

When a group has only one single tab you may want that tab to take the full width.

Tab Reorder Mode

Controls how tabs animate when reordered via drag-and-drop.

ModeBehavior
'default'Tabs swap positions instantly on drop — the original behavior.
'smooth'Tabs slide smoothly to open a gap for the dragged tab, similar to browser tabs.
tabAnimation
Controls tab drag-and-drop reorder animation style. - "smooth": tabs animate smoothly during drag-and-drop reorder — tabs slide apart to reveal the insertion gap, then animate to their final positions on drop (Chrome-like behavior). - "default": standard tab reorder behavior without animation. Defaults to "default".
tabAnimation?: TabAnimation

Tab Groups

Tab groups let you visually organize tabs within a panel using colored chips. Tabs can be grouped, collapsed, expanded, recolored, and reordered via drag-and-drop. Right-click a chip or tab to see available actions.

Creating and Managing Tab Groups

Use DockviewApi to create tab groups and assign panels to them:

// Create a tab group in a specific group panel
const tabGroup = api.createTabGroup({
groupId: 'group-1',
label: 'My Group',
color: 'blue',
});

// Add panels to the tab group
api.addPanelToTabGroup({
groupId: 'group-1',
tabGroupId: tabGroup.id,
panelId: 'panel-1',
});

// Remove a panel from its tab group
api.removePanelFromTabGroup({
groupId: 'group-1',
panelId: 'panel-1',
});

// Dissolve a tab group (removes all panels from it and destroys it)
api.dissolveTabGroup({
groupId: 'group-1',
tabGroupId: tabGroup.id,
});

// Move a tab group to a new position in the tab bar
api.moveTabGroup({
groupId: 'group-1',
tabGroupId: tabGroup.id,
index: 0, // move to the beginning
});

Querying Tab Groups

// Get all tab groups in a group panel
const tabGroups = api.getTabGroups({ groupId: 'group-1' });

// Find which tab group a panel belongs to
const tabGroup = api.getTabGroupForPanel({
groupId: 'group-1',
panelId: 'panel-1',
});

ITabGroup

The ITabGroup interface provides methods to read and modify a tab group:

tabGroup.id; // readonly string
tabGroup.label; // readonly string
tabGroup.color; // readonly string | undefined
tabGroup.collapsed; // readonly boolean
tabGroup.panelIds; // readonly string[]
tabGroup.size; // readonly number (panel count)
tabGroup.isEmpty; // readonly boolean
tabGroup.componentParams; // readonly Record<string, unknown> | undefined

// Modify properties
tabGroup.setLabel('New Label');
tabGroup.setColor('red'); // palette id
tabGroup.setColor('#abc123'); // raw CSS literal
tabGroup.setColor(undefined); // clear
tabGroup.setComponentParams({ icon: 'star' });

// Collapse and expand
tabGroup.collapse();
tabGroup.expand();
tabGroup.toggle();

// Panel membership
tabGroup.containsPanel('panel-1');
tabGroup.indexOfPanel('panel-1');

Color palette

tabGroup.color is any CSS color string — either an id from the active palette or a raw CSS literal ('#abc123', 'rgb(...)'). Resolution to a concrete value is handled by the dockview's color palette.

The default palette ships with 9 named entries:

IdCSS variable
grey--dv-tab-group-color-grey
blue--dv-tab-group-color-blue
red--dv-tab-group-color-red
yellow--dv-tab-group-color-yellow
green--dv-tab-group-color-green
pink--dv-tab-group-color-pink
purple--dv-tab-group-color-purple
cyan--dv-tab-group-color-cyan
orange--dv-tab-group-color-orange

The default palette references these CSS vars, so you can re-skin the defaults purely via CSS overrides. The full set is exported as DEFAULT_TAB_GROUP_COLORS.

Custom palette

Replace the default palette with tabGroupColors. The list fully replaces the defaults — there is no merge. Each entry needs an id (stored on tabGroup.color and serialized) and a value (any CSS color expression). An optional label appears as a tooltip in the context-menu picker.

const tabGroupColors = [
{ id: 'sunset', value: '#ff6b35', label: 'Sunset' },
{ id: 'ocean', value: '#118ab2', label: 'Ocean' },
{ id: 'forest', value: '#06d6a0', label: 'Forest' },
];

The active palette is exposed on the api as api.tabGroupColors for custom chip renderers that want to read from it (e.g. to render their own picker).

Disabling color

Set tabGroupAccent: 'off' to opt out of the built-in color concept entirely. The dockview stops writing the --dv-tab-group-color custom property, hides the color picker, and applies dv-tab-group-chip--accent-off to chips. The tabGroup.color field is still preserved as data — pair this with a createTabGroupChipComponent to own the chip visual end-to-end.

Component params

Use componentParams to attach free-form data to a tab group — typically consumed by a custom chip renderer. The value is a Record<string, unknown> that round-trips through toJSON / fromJSON, so it must be JSON-serializable.

const tabGroup = api.createTabGroup({
groupId: 'group-1',
label: 'Trading',
componentParams: { icon: '📊', priority: 1 },
});

// Update at runtime — fires onDidChange so a custom chip can re-render
tabGroup.setComponentParams({ icon: '⭐️', priority: 1 });

Events

Tab group lifecycle events are available on DockviewApi:

api.onDidCreateTabGroup((event) => { });
api.onDidDestroyTabGroup((event) => { });
api.onDidAddPanelToTabGroup((event) => { });
api.onDidRemovePanelFromTabGroup((event) => { });
api.onDidTabGroupChange((event) => { });
api.onDidTabGroupCollapsedChange((event) => { });

Individual tab groups also expose events:

tabGroup.onDidChange(() => { }); // label or color changed
tabGroup.onDidCollapseChange((collapsed) => { });
tabGroup.onDidPanelChange(({ panelId, type }) => { }); // 'add' | 'remove'
tabGroup.onDidDestroy(() => { });

See Events for the full event reference.

Chip Context Menu

Right-clicking a tab group chip can show a context menu. This is opt-in — no menu is shown unless getTabGroupChipContextMenuItems is provided. Return an empty array to suppress the menu for specific chips.

Built-in items

Pass string shortcuts to render standard menu entries without any extra code:

ValueBehaviour
'rename'An inline text input to rename the tab group
'colorPicker'A grid of color swatches to change the tab group color
'separator'Render a visual divider

Custom label items

Provide an object with a label and action to add a simple clickable entry:

getTabGroupChipContextMenuItems: (params) => [
'rename',
'colorPicker',
'separator',
{ label: 'Dissolve group', action: () => params.api.dissolveTabGroup({ groupId: params.group.id, tabGroupId: params.tabGroup.id }) },
]

Custom Chip Renderer

To fully customize how tab group chips look, provide a createTabGroupChipComponent factory. It receives the ITabGroup and must return an ITabGroupChipRenderer.

The renderer must implement:

interface ITabGroupChipRenderer {
readonly element: HTMLElement; // the chip DOM element
init(params: { tabGroup: ITabGroup; api: DockviewApi }): void;
update?(params: { tabGroup: ITabGroup }): void; // called when label, color or componentParams change
dispose(): void;
}

Read tabGroup.componentParams inside init / update to access free-form data attached to the group, and listen on tabGroup.onDidChange to re-render when the value mutates.

Styling

Tab group appearance can be customized through CSS custom properties:

PropertyDefaultDescription
--dv-tab-group-color-grey#5f6368Color for grey chips
--dv-tab-group-color-blue#1a73e8Color for blue chips
--dv-tab-group-color-red#d93025Color for red chips
--dv-tab-group-color-yellow#f9ab00Color for yellow chips
--dv-tab-group-color-green#188038Color for green chips
--dv-tab-group-color-pink#d01884Color for pink chips
--dv-tab-group-color-purple#a142f4Color for purple chips
--dv-tab-group-color-cyan#007b83Color for cyan chips
--dv-tab-group-color-orange#e8710aColor for orange chips
--dv-tab-group-chip-padding4px 8pxChip padding
--dv-tab-group-chip-border-radius6pxChip border radius
--dv-tab-group-chip-font-size11pxChip font size
--dv-tab-group-line-height2pxHeight of the colored underline beneath grouped tabs
--dv-tab-group-line-opacity0.6Opacity of the colored underline

Serialization

Tab groups are included in toJSON / fromJSON automatically. Each tab group is serialized as:

interface SerializedTabGroup {
id: string;
label?: string;
color?: string;
collapsed: boolean;
panelIds: string[];
componentParams?: Record<string, unknown>;
}

Tab Height

Tab height can be controlled through CSS.

Newsletter