Plugin API References
WARNING
The API is only available in Vue Devtools 6+
Plugin setup
setupDevtoolsPlugin
Registers a devtools plugin. Describes the plugin and provides access to the devtools API.
setupDevtoolsPlugin (pluginDescriptor, setupFn)
The plugin descriptor is an object describing the devtools plugin to the Vue devtools user.
It has the following properties:
id
: a unique id between all possible plugins. It's recommended to use a Reverse Domain Name notation, for example:org.vuejs.router
, or the npm package name.app
: the current application instance. The devtools is scoped to a specific application, so you have to specify on which application instance the devtools plugin is going to work.label
: the label displayed to the user. It's recommended to use a user-friendly name from your plugin, for example:'Vue Router'
. Do not put'devtools'
or'plugin'
in the name, since the user will be seeing this devtools plugin while using your Vue plugin already.packageName
(optional): Thenpm
package name associated with the devtools plugin, for example'vue-router'
.homepage
(optional): URL to your documentation.logo
(optional): URL to a logo of your Vue plugin.componentStateTypes
(optional): an array of custom component state section names you are going to add to the Component inspector. If you add new state to the component inspector, you should declare their sections here so the devtools can display the plugin icon.disableAppScope
(optional): if set totrue
, the hooks registered with this plugin will not be scoped to the associated app. In that case, you might need to use theapp
payload property to check what the current app is inside each hook.disablePluginScope
(optional): if set totrue
, the hooks registered with this plugin will not be scoped to the current plugin. In that case, you might need to use thepluginId
payload property (depending on the hook) to check what the related plugin is inside each hook.enableEarlyProxy
(optional): if set totrue
, the plugin will run even if the Vue devtools are not connected yet using a proxy of the Plugin API and a buffer queue. This is useful if you need to add timeline events before the user opens the devtools.settings
(optional): an object describing the plugin settings. Learn more about plugin settings here.
Example:
const stateType = 'routing properties'
setupDevtoolsPlugin({
id: 'org.vuejs.router',
app,
label: 'Vue Router',
packageName: 'vue-router',
homepage: 'https://router.vuejs.org/',
logo: 'https://vuejs.org/images/icons/favicon-96x96.png',
componentStateTypes: [
stateType
]
}, api => {
// Use the API here
})
Component inspector
on.visitComponentTree
Use this hook to add tags in the component tree.
The payload
argument:
app
: app instance currently active in the devtoolscomponentInstance
: the current component instance data in the treetreeNode
: the tree node that will be sent to the devtoolsfilter
: the current value of the seach input above the tree in the component inspector
Example:
api.on.visitComponentTree(payload => {
const node = payload.treeNode
if (node.name === 'MyApp') {
node.tags.push({
label: 'root',
textColor: 0x000000,
backgroundColor: 0xFF984F
})
} else {
node.tags.push({
label: 'test',
textColor: 0xFFAAAA,
backgroundColor: 0xFFEEEE,
tooltip: `It's a test!`
})
}
})
on.inspectComponent
Use this hook to add new information to the state of the selected component.
The payload
argument:
app
: app instance currently active in the devtoolscomponentInstance
: the current component instance data in the treeinstanceData
: the state that will be sent to the devtools
To add new state, you can push new fields into the instanceData.state
array:
Basic field
type
: name of the section under which the field will appearkey
: name of the fieldvalue
: value of the fieldeditable
(optional): boolean to enable edition
Custom value
By default, the devtools will display your field depending on whether it's an object, an array, etc. You can customize the field display by putting a { _custom: {} }
object to the value.
The _custom
object has the following properties:
type
: Displays the type of the value. Examples:'router'
,'component'
,'service'
...display
: Text displayed instead of the value. Example:'5 minutes'
tooltip
: Tooltip when hovering the value text (display
)value
: Actual valueabstract
: No value is displayed. Useful for indexes. For example,Set
objects have abstract index child fields:0
,1
...readOnly
: mark this value has not editablefields
: an object of configure immediate child fieldsabstract
actions
: an array of buttons to add to the fieldicon
: material icon identifiertooltip
: button tooltipaction
: function to be executed
When you add new sections with the type
property, you should declare them in the componentStateTypes
array in the plugin descriptor when you call the setupDevtoolsPlugin
.
Example:
api.on.inspectComponent(payload => {
if (payload.instanceData) {
payload.instanceData.state.push({
type: stateType,
key: 'foo',
value: 'bar'
})
payload.instanceData.state.push({
type: stateType,
key: 'time',
value: {
_custom: {
type: null,
readOnly: true,
display: `${time}s`,
tooltip: 'Elapsed time',
value: time,
actions: [
{
icon: 'input',
tooltip: 'Log to console',
action: () => console.log('current time:', time)
}
]
}
}
})
}
})
on.editComponentState
If you mark a field as editable: true
, you should also use this hook to apply the new value sent by the devtools.
You have to put a condition in the callback to target only your field type:
api.on.editComponentState(payload => {
if (payload.type === stateType) {
// Edit logic here
}
})
The payload
argument:
app
: app instance currently active in the devtoolstype
: the current field typepath
: an array of string that represents the property edited by the user. For example, if the user edits themyObj.myProp.hello
property, thepath
will be['myObj', 'myProp', 'hello']
.state
: object describing the edit with those properties:value
: new valuenewKey
: string that is set if the key of the value changed, usually when it's in an objectremove
: iftrue
, the value should be removed from the object or array
set
: an helper function that makes it easy to apply the edit on a state object
Example:
api.on.editComponentState(payload => {
if (payload.type === stateType) {
payload.set(myState)
}
})
Here is a full example of an editable custom component field:
const myState = {
foo: 'bar'
}
api.on.inspectComponent(payload => {
if (payload.instanceData) {
payload.instanceData.state.push({
type: stateType,
key: 'foo',
value: myState.foo,
editable: true
})
}
})
api.on.editComponentState(payload => {
if (payload.type === stateType) {
payload.set(myState)
}
})
As you can see, you should use an object to hold the field value so that it can be assigned to.
notifyComponentUpdate
If your state has changed, you can tell the devtools to refresh the selected component state with the notifyComponentUpdate
method:
setInterval(() => {
api.notifyComponentUpdate()
}, 5000)
You can also pass a specific component instance:
api.notifyComponentUpdate(vm)
Custom inspector
Custom inspectors are useful to display debugging information about your library using an inspectable tree.
addInspector
This function registers a new custom inspector.
The options are:
id
: unique custom inspector idlabel
: label displayed in theInspector
sub menuicon
(optional): Material icon code, for example'star'
treeFilterPlaceholder
(optional): placeholder of the filter input above the treestateFilterPlaceholder
(optional): placeholder of the filter input in the state inspectornoSelectionText
(optional): text displayed in the inspector pane when no node is selectedactions
: an array of buttons to add to the header of the inspectoricon
: material icon identifiertooltip
: button tooltipaction
: function to be executed
nodeActions
: an array of buttons to add to the selected node paneicon
: material icon identifiertooltip
: button tooltipaction
: function to be executed
Example:
const INSPECTOR_ID = 'test-inspector'
api.addInspector({
id: INSPECTOR_ID,
label: 'Test inspector',
icon: 'tab_unselected',
treeFilterPlaceholder: 'Search for test...',
actions: [
{
icon: 'star',
tooltip: 'Test custom action',
action: () => console.log('Meow! 🐱')
}
],
nodeActions: [
{
icon: 'star',
tooltip: 'Test node custom action',
action: (nodeId) => console.log('Node action:', nodeId)
}
]
})
TIP
It's recommended to use a variable to put the id
, so that you can reuse it afterwards.
on.getInspectorTree
This hook is called when the devtools wants to load the tree of any custom inspector.
You have to put a condition in the callback to target only your inspector:
api.on.getInspectorTree(payload => {
if (payload.inspectorId === 'test-inspector') {
// Your logic here
}
})
The payload
argument:
app
: app instance currently active in the devtoolsinspectorId
: id of the current custom inspectorfilter
: string of the user input in the search fieldrootNodes
: array of root nodes of the tree you want to display in the devtools
Each node can have those properties:
id
: a unique node idlabel
: the text displayed in the treechildren
(optional): an array of child nodestags
(optional): an array of tag objects:label
: text displayed in the tagtextColor
: text color, for example:0x000000
for blackbackgroundColor
: background color, for example:0xffffff
for whitetooltip
(optional): HTML for a tooltip over the tag
Example:
api.on.getInspectorTree(payload => {
if (payload.inspectorId === 'test-inspector') {
payload.rootNodes = [
{
id: 'root',
label: `Root (${time})`,
children: [
{
id: 'child',
label: `Child ${payload.filter}`,
tags: [
{
label: 'active',
textColor: 0x000000,
backgroundColor: 0xFF984F
},
{
label: 'test',
textColor: 0xffffff,
backgroundColor: 0x000000
}
]
}
]
}
]
}
})
on.getInspectorState
This hook is called when the devtools needs to load the state for the currently selected node in a custom inspector.
You have to put a condition in the callback to target only your inspector:
api.on.getInspectorState(payload => {
if (payload.inspectorId === 'test-inspector') {
// Your logic here
}
})
The payload
argument:
app
: app instance currently active in the devtoolsinspectorId
: id of the current custom inspectornodeId
: id of the currently selected nodestate
: state sent to the devtools
The state is an object, which keys are the section names in the state inspector, and the value is an array of fields:
payload.state = {
'section 1': [
// fields
],
'section 2': [
// fields
]
}
Each field is an object with:
type
: name of the section under which the field will appearkey
: name of the fieldvalue
: value of the fieldeditable
(optional): boolean to enable edition
You can also use a Custom value.
Example:
api.on.getInspectorState(payload => {
if (payload.inspectorId === 'test-inspector') {
if (payload.nodeId === 'root') {
payload.state = {
'root info': [
{
key: 'foo',
value: myState.foo,
editable: true
},
{
key: 'time',
value: time
}
]
}
} else {
payload.state = {
'child info': [
{
key: 'answer',
value: {
_custom: {
display: '42!!!',
value: 42,
tooltip: 'The answer'
}
}
}
]
}
}
}
})
on.editInspectorState
If you mark a field as editable: true
, you should also use this hook to apply the new value sent by the devtools.
You have to put a condition in the callback to target only your inspector:
api.on.editInspectorState(payload => {
if (payload.inspectorId === 'test-inspector') {
// Edit logic here
}
})
The payload
argument:
app
: app instance currently active in the devtoolsinspectorId
: id of the current custom inspectornodeId
: id of the currently selected nodetype
: the current field typepath
: an array of string that represents the property edited by the user. For example, if the user edits themyObj.myProp.hello
property, thepath
will be['myObj', 'myProp', 'hello']
.state
: object describing the edit with those properties:value
: new valuenewKey
: string that is set if the key of the value changed, usually when it's in an objectremove
: iftrue
, the value should be removed from the object or array
set
: an helper function that makes it easy to apply the edit on a state object
Example:
api.on.editInspectorState(payload => {
if (payload.inspectorId === 'test-inspector') {
if (payload.nodeId === 'root') {
payload.set(myState)
}
}
})
sendInspectorTree
If you need to update the tree to the user, call this function to ask for a refresh.
Example:
setInterval(() => {
api.sendInspectorTree('test-inspector')
}, 5000)
sendInspectorState
If you need to update the currently selected node state to the user, call this function to ask for a refresh.
Example:
setInterval(() => {
api.sendInspectorState('test-inspector')
}, 5000)
selectInspectorNode
Select a specific node in the inspector tree. The arguments are:
inspectorId
: the id of your inspectornodeId
: the id of the node to be selected
Example:
api.selectInspectorNode('test-inspector', 'some-node-id')
Timeline
now
Returns the current time with the maximum available precision.
api.now()
addTimelineLayer
Register a new timeline layer with this method. The options are:
id
: unique id of the layer. It's recommended to use a variable to store it.label
: text displayed in the layer listcolor
: color of the layer background and event graphicsskipScreenshots
(optional): don't trigger a screenshot for the layer eventsgroupsOnly
(optional): only display groups of events (they will be drawn as rectangles)ignoreNoDurationGroups
(optional): skip groups with no duration (useful whengroupsOnly
istrue
)
Example:
api.addTimelineLayer({
id: 'test-layer',
label: 'Test layer',
color: 0x92A2BF
})
addTimelineEvent
Use this function to send a new event on the timeline.
layerId
: id of the layerevent
: event objecttime
: time in millisecond when the event happeneddata
: state displayed when selecting the eventtitle
(optional): text displayed in the event listsubtitle
(optional): secondary text displayed in the event listlogType
(optional): either'default'
,'warning'
or'error'
meta
(optional): object where you can store metadata about the object that will not be displayed when it's selectedgroupId
(optional): id used to group multiple event together
Example:
api.addTimelineEvent({
layerId: 'test-layer',
event: {
time: api.now(),
data: {
info: 'window.keyup',
key: event.key
},
groupId: event.key,
title: 'Group test',
meta: {
foo: 'bar'
}
}
})
on.inspectTimelineEvent
This hook is called when a timline event is selected. It's useful if you want to send additional information to the devtools in a lazy way.
You have to put a condition in the callback to target only your timeline layer:
api.on.inspectTimelineEvent(payload => {
if (payload.layerId === 'test-layer') {
// Your logic here
}
})
Example:
api.on.inspectTimelineEvent(payload => {
if (payload.layerId === 'test-layer') {
// Async operation example
return new Promise(resolve => {
setTimeout(() => {
payload.data = {
...payload.data,
hey: 'hello'
}
resolve()
}, 1000)
})
}
})
on.timelineCleared
This hook is called when the timeline is cleared by the user. Note that clearing the timeline affects all apps and layers simultaneously.
api.on.timelineCleared(() => {
console.log('timeline is cleared!')
})
Settings
Plugin settings allow the user to customize the plugin behavior. Learn more about plugin settings here.
getSettings
Get the current plugin settings.
Example:
api.getSettings()
on.setPluginSettings
Hook called when the user changes the plugin settings.
Payload properties:
key
: settings itemnewValue
: new value for the changed settingsoldValue
: its old value (deep clone)settings
: the whole current settings state object
// Plugin settings change
api.on.setPluginSettings(payload => {
console.log('plugin settings changed', payload.settings,
// Info about the change
payload.key, payload.newValue, payload.oldValue)
})
Utilities
getComponentInstances
Component instances on the Vue app.
app
: the target Vue app instance
Example:
let componentInstances = []
api.on.getInspectorTree(async (payload) => {
if (payload.inspectorId === 'test-inspector') { // e.g. custom inspector
componentInstances = await api.getComponentInstances(app)
for (const instance of instances) {
payload.rootNodes.push({
id: instance.uid.toString(),
label: `Component ${instance.uid}`
})
}
// something todo ...
}
})
getComponentBounds
Computes the component bounds on the page.
Example:
api.on.inspectComponent(async payload => {
if (payload.instanceData) {
const bounds = await api.getComponentBounds(payload.componentInstance)
payload.instanceData.state.push({
type: stateType,
key: 'bounds',
value: bounds ? {
left: bounds.left,
top: bounds.top,
width: bounds.width,
height: bounds.height
} : null
})
}
})
getComponentName
Retrieves the component name.
Example:
api.on.inspectComponent(async payload => {
if (payload.instanceData) {
const componentName = await api.getComponentName(payload.componentInstance)
payload.instanceData.state.push({
type: stateType,
key: 'component name',
value: componentName
})
}
})
highlightElement
Highlight the element of the component.
instance
: the target component instance
Example:
let componentInstances = [] // keeped component instance of the Vue app (e.g. `getComponentInstances`)
api.on.getInspectorState(payload => {
if (payload.inspectorId === 'test-inspector') { // e.g. custom inspector
// find component instance from custom inspector node
const instance = componentInstances.find(instance => instance.uid.toString() === payload.nodeId)
if (instance) {
api.highlightElement(instance)
}
// something todo ...
}
})
unhighlightElement
Unhighlight the element.
instance
: the target component instance
Example:
let componentInstances = [] // keeped component instance of the Vue app (e.g. `getComponentInstances`)
api.on.getInspectorState(payload => {
if (payload.inspectorId === 'test-inspector') { // e.g. custom inspector
// find component instance from custom inspector node
const instance = componentInstances.find(instance => instance.uid.toString() === payload.nodeId)
if (instance) {
api.unhighlightElement(instance)
}
// something todo ...
}
})