Skip to main content
Version: 1.8.4

Dockview

Introduction

Dockview is an abstraction built on top of Gridviews where each view is a container of many tabbed panels.


You can access the panels associated group through the panel.group variable. The group will always be defined and will change if a panel is moved into another group.

DockviewReact Component

You can create a Dockview through the use of the DockviewReact component.

All of these are React props available through the DockviewReact component.

className
string
components
PanelCollection<IDockviewPanelProps<any>>
defaultTabComponent
FunctionComponent<IDockviewPanelHeaderProps<any>>
disableAutoResizing
boolean
disableFloatingGroups
boolean
floatingGroupBounds
{ minimumHeightWithinViewport: number, minimumWidthWithinViewport: number } | 'boundedWithinViewport'
hideBorders
boolean
leftHeaderActionsComponent
FunctionComponent<IDockviewHeaderActionsProps>
onDidDrop
(event: DockviewDropEvent): void
onReady
(event: DockviewReadyEvent): void
prefixHeaderActionsComponent
FunctionComponent<IDockviewHeaderActionsProps>
rightHeaderActionsComponent
FunctionComponent<IDockviewHeaderActionsProps>
showDndOverlay
(event: DockviewDndOverlayEvent): boolean
singleTabMode
'fullwidth' | 'default'
tabComponents
PanelCollection<IDockviewPanelHeaderProps<any>>
watermarkComponent
FunctionComponent<IWatermarkPanelProps>

Dockview API

The Dockview API is exposed both at the onReady event and on each panel through props.containerApi. Through this API you can control general features of the component and access all added panels.

Dockview API via Panel component
const MyComponent = (props: IDockviewPanelProps<{ title: string }>) => {
// props.containerApi...

return <div>{`My first panel has the title: ${props.params.title}`}</div>;
};
Dockview API via the onReady callback
const onReady = (event: DockviewReadyEvent) => {
// event.api...
};

All of these methods are available through the api property of DockviewComponent and the containerApi property of IDockviewPanel.

activeGroup
Active group object.
DockviewGroupPanel | undefined
activePanel
Active panel object.
IDockviewPanel | undefined
groups
All group objects.
DockviewGroupPanel[]
height
Height of the component.
number
id
The unique identifier for this instance. Used to manage scope of Drag'n'Drop events.
string
maximumHeight
Maximum height of the component.
number
maximumWidth
Maximum width of the component.
number
minimumHeight
Minimum height of the component.
number
minimumWidth
Minimum width of the component.
number
onDidActiveGroupChange
Invoked when the active group changes. May be undefined if no group is active.
Event<DockviewGroupPanel | undefined>
onDidActivePanelChange
Invoked when the active panel changes. May be undefined if no panel is active.
Event<IDockviewPanel | undefined>
onDidAddGroup
Invoked when a group is added. May be called multiple times when moving groups.
Event<DockviewGroupPanel>
onDidAddPanel
Invoked when a panel is added. May be called multiple times when moving panels.
Event<IDockviewPanel>
onDidDrop
Invoked when a Drag'n'Drop event occurs that the component was unable to handle. Exposed for custom Drag'n'Drop functionality.
Event<DockviewDropEvent>
onDidLayoutChange
Invoked when any layout change occures, an aggregation of many events.
Event<void>
onDidLayoutFromJSON
Invoked after a layout is deserialzied using the fromJSON method.
Event<void>
onDidRemoveGroup
Invoked when a group is removed. May be called multiple times when moving groups.
Event<DockviewGroupPanel>
onDidRemovePanel
Invoked when a panel is removed. May be called multiple times when moving panels.
Event<IDockviewPanel>
onWillDragGroup
Invoked before a group is dragged. Exposed for custom Drag'n'Drop functionality.
Event<GroupDragEvent>
onWillDragPanel
Invoked before a panel is dragged. Exposed for custom Drag'n'Drop functionality.
Event<TabDragEvent>
panels
All panel objects.
IDockviewPanel[]
size
Total number of groups.
number
totalPanels
Total number of panels.
number
width
Width of the component.
number
addFloatingGroup
Add a floating group
(item: IDockviewPanel | DockviewGroupPanel, coord?: { x: number, y: number }): void
addGroup
Add a group and return the created object.
(options?: AddGroupOptions): DockviewGroupPanel
addPanel
Add a panel and return the created object.
<T extends object = Parameters>(options: AddPanelOptions<T>): IDockviewPanel
clear
Reset the component back to an empty and default state.
(): void
closeAllGroups
Close all groups and panels.
(): void
focus
Focus the component. Will try to focus an active panel if one exists.
(): void
fromJSON
Create a component from a serialized object.
(data: SerializedDockview): void
getGroup
Get a group object given a string id. May return undefined.
(id: string): DockviewGroupPanel | undefined
getPanel
Get a panel object given a string id. May return undefined.
(id: string): IDockviewPanel | undefined
layout
Force resize the component to an exact width and height. Read about auto-resizing before using.
(width: number, height: number, force: boolean = false): void
moveToNext
Move the focus progmatically to the next panel or group.
(options?: MovementOptions): void
moveToPrevious
Move the focus progmatically to the previous panel or group.
(options?: MovementOptions): void
removeGroup
Remove a group and any panels within the group.
(group: IDockviewGroupPanel): void
removePanel
Remove a panel given the panel object.
(panel: IDockviewPanel): void
toJSON
Create a serialized object of the current component.
(): SerializedDockview

Dockview Panel API

const MyComponent = (props: IDockviewPanelProps<{ title: string }>) => {
// props.api...

return <div>{`My first panel has the title: ${props.params.title}`}</div>;
};

All of these are methods are available through the api property of IDockviewPanel.

group
DockviewGroupPanel
height
The panel height in pixels
number
id
The id of the panel that would have been assigned when the panel was created
string
isActive
Whether the panel is the actively selected panel
boolean
isFocused
Whether the panel holds the current focus
boolean
isGroupActive
boolean
isVisible
Whether the panel is visible
boolean
onDidActiveChange
Event<ActiveEvent>
onDidActiveGroupChange
Event<void>
onDidDimensionsChange
Event<PanelDimensionChangeEvent>
onDidFocusChange
Event<FocusEvent>
onDidGroupChange
Event<void>
onDidVisibilityChange
Event<VisibilityEvent>
title
string | undefined
width
The panel width in pixels
number
close
(): void
moveTo
(options: { group: DockviewGroupPanel, index: number, position: Position }): void
setActive
(): void
setSize
(event: SizeEvent): void
setTitle
(title: string): void
updateParameters
(parameters: Parameters): void

Theme

As well as importing the dockview stylesheet you must provide a class-based theme somewhere in your application. For example.

// Providing a theme directly through the DockviewReact component props
<DockviewReact className="dockview-theme-dark" />

// Providing a theme somewhere in the DOM tree
<div className="dockview-theme-dark">
<div>
{/**... */}
<DockviewReact />
</div>
</div>

You can find more details on theming here.

Layout Persistance

Layouts are loaded and saved via to fromJSON and toJSON methods on the Dockview api. The api also exposes an event onDidLayoutChange you can listen on to determine when the layout has changed. Below are some snippets showing how you might load from and save to localStorage.

Saving the layout state to localStorage
React.useEffect(() => {
if (!api) {
return;
}

const disposable = api.onDidLayoutChange(() => {
const layout = api.toJSON();

localStorage.setItem(
'dockview_persistance_layout',
JSON.stringify(layout)
);
});

return () => {
disposable.dispose();
};
}, [api]);
Loading a layout from localStorage
const onReady = (event: DockviewReadyEvent) => {
const layoutString = localStorage.getItem('dockview_persistance_layout');

let success = false;

if (layoutString) {
try {
const layout = JSON.parse(layoutString);
event.api.fromJSON(layout);
success = true;
} catch (err) {
//
}
}

if (!success) {
// do something if there is no layout or there was a loading error
}
};

Here is an example using the above code loading from and saving to localStorage. If you refresh the page you should notice your layout is loaded as you left it.

Resizing

Panel Resizing

Each Dockview contains of a number of groups and each group has a number of panels. Logically a user may want to resize a panel, but this translates to resizing the group which contains that panel.

You can set the size of a panel using props.api.setSize(...). You can also set the size of the group associated with the panel using props.api.group.api.setSize(...) although this isn't recommended due to the clunky syntax.

// it's mandatory to provide either a height or a width, providing both is optional
props.api.setSize({
height: 100,
width: 200,
});

// you could also resize the panels group, although not recommended it achieved the same result
props.api.group.api.setSize({
height: 100,
width: 200,
});

You can see an example invoking both approaches below.

Container Resizing

The component will automatically resize to it's container.

Watermark

When the dockview is empty you may want to display some fallback content, this is refered to as the watermark. By default there the watermark has no content but you can provide as a prop to DockviewReact a watermarkComponent which will be rendered when there are no panels or groups.

Drag And Drop

Built-in behaviours

Dockview supports a wide variety of built-in Drag and Drop possibilities. Below are some examples of the operations you can perform.

Drag a tab onto another tab to place it inbetween existing tabs.

Drag a tab to the right of the last tab to place it after the existing tabs.

Drag a group onto an existing group to merge the two groups.

Drag into the left/right/top/bottom target zone of a panel to create a new group in the selected direction.

Drag into the center of a panel to add to that group.

Drag to the edge of the dockview component to create a new group on the selected edge.

Extended behaviours

For interaction with the Drag events directly the component exposes some method to help determine whether external drag events should be interacted with or not.

/**
* called when an ondrop event which does not originate from the dockview libray and
* passes the showDndOverlay condition occurs
**/
const onDidDrop = (event: DockviewDropEvent) => {
const { group } = event;

event.api.addPanel({
id: 'test',
component: 'default',
position: {
referencePanel: group.activePanel.id,
direction: 'within',
},
});
};

/**
* called for drag over events which do not originate from the dockview library
* allowing the developer to decide where the overlay should be shown for a
* particular drag event
**/
const showDndOverlay = (event: DockviewDndOverlayEvent) => {
return true;
};

return (
<DockviewReact
components={components}
onReady={onReady}
className="dockview-theme-abyss"
onDidDrop={onDidDrop}
showDndOverlay={showDndOverlay}
/>
);

Intercepting Drag Events

You can intercept drag events to attach your own metadata using the onWillDragPanel and onWillDragGroup api methods.

Third Party Dnd Libraries

This shows a simple example of a third-party library used inside a panel that relies on drag and drop functionalities. This examples serves to show that dockview doesn't interfer with any drag and drop logic for other controls.

Floating Groups

Dockview has built-in support for floating groups. Each floating container can contain a single group with many panels and you can have as many floating containers as needed. You cannot dock multiple groups together in the same floating container.

Floating groups can be interacted with whilst holding the shift key activating the event.shiftKey boolean property on KeyboardEvent events.

Float an existing tab by holding shift whilst interacting with the tab

Move a floating tab by holding shift whilst moving the cursor or dragging the empty header space

Move an entire floating group by holding shift whilst dragging the empty header space

Floating groups can be programatically added through the dockview api method api.addFloatingGroup(...) and you can check whether a group is floating via the group.api.isFloating property. See examples for full code.

You can control the bounding box of floating groups through the optional floatingGroupBounds options:

  • boundedWithinViewport will force the entire floating group to be bounded within the docks viewport.
  • {minimumHeightWithinViewport?: number, minimumWidthWithinViewport?: number} sets the respective dimension minimums that must appears within the docks viewport
  • If no options are provided the defaults of 100px minimum height and width within the viewport are set.

Panels

Add Panel

Using the dockview API you can access the addPanel method which returns an instance of the created panel. The minimum method signature is:

const panel = api.addPanel({
id: 'my_unique_panel_id',
component: 'my_component',
});

where id is the unique id of the panel and component is the implenentation which will be used to render the panel. You will have registered this using the components prop of the DockviewReactComponent component.

You can optionally provide a tabComponent parameters to the addPanel method which will render the tab using a custom renderer. You will have registered this using the tabComponents prop of the DockviewReactComponent component.

const panel = api.addPanel({
id: 'my_unique_panel_id',
component: 'my_component',
tabComponent: 'my_tab_component',
});

You can pass properties to the panel using the params key. You can update these properties through the panels api object and its updateParameters method.

const panel = api.addPanel({
id: 'my_unique_panel_id',
component: 'my_component',
params: {
myCustomKey: 'my_custom_value',
},
});

panel.api.updateParameters({
myCustomKey: 'my_custom_value',
myOtherCustomKey: 'my_other_custom_key',
});

Note updateParameters does not accept partial parameter updates, you should call it with the entire set of parameters you want the panel to receive.

Finally addPanel accepts a position object which tells dockview where to place the panel.

  • This object optionally accepts either a referencePanel or referenceGroup which can be the associated id as a string or the panel/group object reference.
  • This object accepts a direction property which dictates where, relative to the provided reference the new panel will be placed.

If neither a referencePanel or referenceGroup is provided then the direction will be treated as absolute.

If no direction is provided the library will place the new panel in a pre-determined position.

const panel = api.addPanel({
id: 'panel_1',
component: 'default',
});

const panel2 = api.addPanel({
id: 'panel_2',
component: 'default',
position: {
referencePanel: panel1,
direction: 'right',
},
});

To add a floating panel you should include the floating variable which can be either a boolean or an object defining it's bounds. These bounds are relative to the dockview component.

const panel1 = api.addPanel({
id: 'panel_2',
component: 'default',
floating: true,
});

const panel2 = api.addPanel({
id: 'panel_2',
component: 'default',
floating: { x: 10, y: 10, width: 300, height: 300 },
});

Update Panel

You can programatically update the params passed through to the panel through the panal api using api.updateParameters.

const panel = api.addPanel({
id: 'panel_1',
component: 'default',
params: {
keyA: 'valueA',
},
});

// ...

panel.api.updateParameters({
keyB: 'valueB',
});

// ...

panel.api.updateParameters({
keyA: 'anotherValueA',
});

To delete a parameter you should pass a value of undefined for the key.

panel.api.updateParameters({
keyA: undefined, // this will delete 'keyA'.
});

Move panel

You can programatically move a panel using the panel api.

panel.api.moveTo({ group, position, index });

An equivalent method for moving groups is avaliable on the group api.

const group = panel.api.group;
group.api.moveTo({ group, position });

Remove panel

You can programatically remove a panel using the panel api.

panel.api.close();

Given a reference to the panel you can also use the component api to remove it.

const panel = api.getPanel('myPanel');
api.removePanel(panel);

Panel Rendering

By default DockviewReact only adds to the DOM those panels that are visible, if a panel is not the active tab and not shown the contents of the hidden panel will be removed from the DOM.

However the React Components associated with each panel are only created once and will always exist for as long as the panel exists, hidden or not.

For example this means that any hooks in those components will run whether the panel is visible or not which may lead to excessive background work depending on the panels implementation.

This is the default behaviour to ensure the greatest flexibility for the user but through the panels props.api you can listen to the visiblity state of the panel and write additional logic to optimize your application.

For example if you wanted to unmount the React Components when the panel is not visible you could create a Higher-Order-Component that listens to the panels visiblity state and only renders the panel when visible.

Only rendering the React Component when the panel is visible, otherwise rendering a null React Component
import { IDockviewPanelProps } from 'dockview';
import * as React from 'react';

function RenderWhenVisible(
component: React.FunctionComponent<IDockviewPanelProps>
) {
const HigherOrderComponent = (props: IDockviewPanelProps) => {
const [visible, setVisible] = React.useState<boolean>(
props.api.isVisible
);

React.useEffect(() => {
const disposable = props.api.onDidVisibilityChange((event) =>
setVisible(event.isVisible)
);

return () => {
disposable.dispose();
};
}, [props.api]);

if (!visible) {
return null;
}

return React.createElement(component, props);
};
return HigherOrderComponent;
}
const components = { default: RenderWhenVisible(MyComponent) };

Toggling the checkbox you can see that when you only render those panels which are visible the underling React component is destroyed when it becomes hidden and re-created when it becomes visible.

Headers

Custom Tab Headers

You can provide custom renderers for your tab headers for maximum customization. A default implementation of DockviewDefaultTab is provided should you only wish to attach minor changes and events that do not alter the default behaviour, for example to add a custom context menu event handler.

The DockviewDefaulTab component accepts a hideClose prop if you wish only to hide the close button.

Attaching a custom context menu event handlers to a custom header
import { IDockviewPanelHeaderProps, DockviewDefaultTab } from 'dockview';

const MyCustomheader = (props: IDockviewPanelHeaderProps) => {
const onContextMenu = (event: React.MouseEvent) => {
event.preventDefault();
alert('context menu');
};
return <DockviewDefaultTab onContextMenu={onContextMenu} {...props} />;
};

You are also free to define a custom renderer entirely from scratch and not make use of the DockviewDefaultTab component. To use a custom renderer you can must register a collection of tab components.

const tabComponents = {
myCustomHeader: MyCustomHeader,
};

return <DockviewReact tabComponents={tabComponents} ... />;
api.addPanel({
id: 'panel_1',
component: 'default',
tabComponent: 'myCustomHeader', // <-- your registered renderers
title: 'Panel 1',
});

You can also override the default tab renderer which will be used when no tabComponent is provided to the addPanel function.

<DockviewReact defaultTabComponent={MyCustomHeader}  ... />;

As a simple example the below attaches a custom event handler for the context menu on all tabs as a default tab renderer

The below example uses a custom tab renderer to reigster a popover when the user right clicked on a tab. This still makes use of the DockviewDefaultTab since it's only a minor change.

Default Tab Title

If you are using the default tab renderer you can set the title of a tab when creating it

api.addPanel({
id: 'panel_1',
component: 'my_component',
title: 'my_custom_title', // <-- special param for title
});

You can update the title through the panel api which can be accessed via props.api if you are inside the panel component or via api.getPanel('panel1').api if you are accessing from outside of the panel component.

api.setTitle('my_new_custom_title');

Note this only works when using the default tab implementation.

Custom Tab Title

If you are using a custom tab implementation you should pass variables through as a parameter and render them through your tab components implementation.

Add a panel with custom parameters
api.addPanel({
id: 'panel_2',
component: 'my_component',
tabComponent: 'my_tab',
params: {
myTitle: 'Window 2', // <-- passing a variable to use as a title
},
});
Accessing custom parameters from a custom tab renderer
const tabComponents = {
default: (props: IDockviewPanelHeaderProps<{ myTitle: string }>) => {
const title = props.params.myTitle; // <-- accessing my custom varaible
return <div>{/** tab implementation as chosen by developer */}</div>;
},
};

Hidden Headers

You may wish to hide the header section of a group. This can achieved through the hidden variable on panel.group.header.

panel.group.header.hidden = true;

Full width tabs

DockviewReactComponent accepts the prop singleTabMode. If set singleTabMode=fullwidth then when there is only one tab in a group this tab will expand to the entire width of the group. For example:

This can be conmbined with Locked Groups to create an application that feels more like a Window Manager rather than a collection of groups and tabs.

<DockviewReactComponent singleTabMode="fullwidth" {...otherProps} />

Tab Height

Tab height can be controlled through CSS.

Groups

Locked group

Locking a group will disable all drop events for this group ensuring no additional panels can be added to the group through drop events. You can still add groups to a locked panel programatically using the API though.

panel.group.locked = true;

// Or

panel.group.locked = 'no-drop-target';

Use true to keep drop zones top, right, bottom, left for the group. Use no-drop-target to disable all drop zones. For you to get a better understanding of what this means, try and drag the panels in the example below to the locked groups.

Group Controls Panel

DockviewReact accepts leftHeaderActionsComponent, rightHeaderActionsComponent and prefixHeaderActionsComponent which expect a React component with props IDockviewHeaderActionsProps. These controls are rendered to left and right side of the space to the right of the tabs in the header bar as well as before the first tab in the case of the prefix header prop.

const Component: React.FunctionComponent<IDockviewHeaderActionsProps> = () => {
return <div>{'...'}</div>;
};

return <DockviewReact {...props} leftHeaderActionsComponent={Component} rightHeaderActionsComponent={...} />;

As a simple example the below uses the groupControlComponent to render a small control that indicates whether the group is active and which panel is active in that group.

const RightHeaderActionsComponent = (props: IDockviewHeaderActionsProps) => {
const isGroupActive = props.isGroupActive;
const activePanel = props.activePanel;

return (
<div className="dockview-groupcontrol-demo">
<span
className="dockview-groupcontrol-demo-group-active"
style={{
background: isGroupActive ? 'green' : 'red',
}}
>
{isGroupActive ? 'Group Active' : 'Group Inactive'}
</span>
<span className="dockview-groupcontrol-demo-active-panel">{`activePanel: ${
activePanel?.id || 'null'
}`}</span>
</div>
);
};

Constraints

You may wish to specify a minimum or maximum height or width for a group which can be done through the group api.

api.group.api.setConstraints(...)

Constraints are currently only supported for groups and not individual panels. If you specific a constraint on a group and move a panel within that group to another group it will no longer be subject to those constraints since those constraints were on the group and not on the individual panel.

iFrames

iFrames required special attention because of a particular behaviour in how iFrames render:

Re-parenting an iFrame will reload the contents of the iFrame or the rephrase this, moving an iFrame within the DOM will cause a reload of its contents.

You can find many examples of discussions on this. Two reputable forums for example are linked here and here.

The problem with iFrames and dockview is that when you hide or move a panel that panels DOM element may be moved within the DOM or removed from the DOM completely. If your panel contains an iFrame then that iFrame will reload after being re-positioned within the DOM tree and all state in that iFrame will most likely be lost.

dockview does not provide a built-in solution to this because it's too specific of a problem to include in the library. However the below example does show an implementation of a higher-order component HoistedDockviewPanelthat you could use to work around this problems and make iFrames behave in dockview.

What the higher-order component is doing is to hoist the panels contents into a DOM element that is always present and then position: absolute that element to match the dimensions of it's linked panel. The visibility of these hoisted elements is then controlled through some exposed api methods to hide elements that shouldn't be currently shown.

You should open this example in CodeSandbox using the provided link to understand the code and make use of this implemention if required.

Events

A simple example showing events fired by `dockviewz that can be interacted with.

Keyboard Navigation

Keyboard shortcuts

Nested Dockviews

You can safely create multiple dockview instances within one page and nest dockviews within other dockviews. If you wish to interact with the drop event from one dockview instance in another dockview instance you can implement the showDndOverlay and onDidDrop props on DockviewReact.

Window-like mananger with tabs