Utility Kit

Utility kit for easier DevTools integrations.

Since v0.3.0, we are now providing a utility kit for easier DevTools integrations, similar to @nuxt/kit.

npm i @nuxt/devtools-kit
import { addCustomTab } from '@nuxt/devtools-kit'

We recommend module authors to install @nuxt/devtools-kit as a dependency and @nuxt/devtools as a dev dependency.

@nuxt/devtools-kit

addCustomTab()

Deprecated.addCustomTab() is soft-deprecated (NDT_DEP_0005) in favour of registering a dock entry on the Vite DevTools docks host (nuxt.devtools.docks.register(...)). It still works as a shim. Note the docks host does not yet cover vnode views or tab categories. See the migration guide.

A shorthand for calling the hook devtools:customTabs.

import { addCustomTab } from '@nuxt/devtools-kit'

addCustomTab(() => ({
  // unique identifier
  name: 'my-module',
  // title to display in the tab
  title: 'My Module',
  // any icon from Iconify, or a URL to an image
  icon: 'carbon:apps',
  // iframe view
  view: {
    type: 'iframe',
    src: '/url-to-your-module-view',
  },
}))

The iframe view supports the following options:

  • src: URL of the iframe
  • persistent: Whether to persist the iframe instance when switching tabs (default: true)
  • permissions: Additional permissions to allow in the iframe (merged with default clipboard-write and clipboard-read)
const view: ModuleIframeView = {
  type: 'iframe',
  src: '/url-to-your-module-view',
  persistent: true,
  permissions: ['camera', 'microphone'],
}

refreshCustomTabs()

Deprecated.refreshCustomTabs() is soft-deprecated (NDT_DEP_0006). Update dock entries directly via the handle returned by nuxt.devtools.docks.register(...). It still works as a shim. See the migration guide.

A shorthand for call hook devtools:customTabs:refresh. It will refresh all custom tabs.

startSubprocess()

Deprecated.startSubprocess() is soft-deprecated (NDT_DEP_0004) in favour of the Vite DevTools terminals host (nuxt.devtools.terminals.startChildProcess(...)). It still works as a shim. See the migration guide.

Start a sub process using tinyexec and create a terminal tab in DevTools.

import { startSubprocess } from '@nuxt/devtools-kit'

const subprocess = startSubprocess(
  {
    command: 'code-server',
    args: [
      'serve-local',
      '--accept-server-license-terms',
      '--without-connection-token',
      `--port=${port}`,
    ],
  },
  {
    id: 'devtools:vscode',
    name: 'VS Code Server',
    icon: 'logos-visual-studio-code',
  },
)
subprocess.restart()
subprocess.terminate()

extendServerRpc()

Deprecated.extendServerRpc() is soft-deprecated (NDT_DEP_0003) in favour of the Vite DevTools RPC registration. It still works as a shim. See the migration guide.

Extend the server RPC with your own methods.

import { extendServerRpc } from '@nuxt/devtools-kit'

const rpc = extendServerRpc('my-module', {
  async myMethod() {
    return 'hello'
  },
})

The forward path registers functions on the Vite DevTools RPC host from the onDevtoolsReady() hook:

import { onDevtoolsReady } from '@nuxt/devtools-kit'
import { defineRpcFunction } from '@vitejs/devtools-kit'

export default defineNuxtModule({
  setup(_options, nuxt) {
    onDevtoolsReady((ctx) => {
      ctx.rpc.register(defineRpcFunction({
        name: 'my-module:my-method',
        type: 'query',
        setup: () => ({ handler: async () => 'hello' }),
      }))
    })
  },
})

Learn more about Custom RPC functions.

onDevtoolsReady()

The recommended entry point for DevTools integration. The callback runs once the Vite DevTools kit has connected and receives the connected ViteDevToolsNodeContext, so the full devframe surface is guaranteed to be available — no connect-safe buffering needed. Do all your DevTools integration here:

import { onDevtoolsReady } from '@nuxt/devtools-kit'
import { defineRpcFunction } from '@vitejs/devtools-kit'

export default defineNuxtModule({
  setup(_options, nuxt) {
    onDevtoolsReady((ctx) => {
      // `ctx` is the connected ViteDevToolsNodeContext
      ctx.docks.register({
        id: 'my-module',
        title: 'My Module',
        type: 'iframe',
        url: '/__my-module__/',
      })

      ctx.rpc.register(defineRpcFunction({
        name: 'my-module:my-method',
        type: 'query',
        setup: () => ({ handler: async () => 'hello' }),
      }))

      ctx.messages.info('My module is ready')
    })
  },
})

It is backed by the devtools:ready Nuxt hook:

nuxt.hook('devtools:ready', (ctx) => {
  // ...
})

Inside onDevtoolsReady(), the ctx is the connected ViteDevToolsNodeContext, which exposes the full devframe-native surface:

  • ctx.docks — register dock entries (iframe / action / json-render / launcher)
  • ctx.terminals — spawn and manage child processes / PTY sessions
  • ctx.messages — structured messages and toast notifications
  • ctx.commands — command-palette entries
  • ctx.diagnostics — structured, coded diagnostics (powered by nostics)
  • ctx.rpc — the devframe RPC host (register/invokeLocal/broadcast/sharedState/streaming/…)

If you need the raw context outside the hook, nuxt.devtools.devtoolsKit is available as an escape hatch (it is undefined until the kit connects).

See the Vite DevTools Kit docs for the full host API.

Deprecation diagnostics

Nuxt DevTools emits coded deprecation diagnostics (NDT_DEP_xxxx) through nostics. They print to the terminal and, once the Vite DevTools kit connects, also surface in the DevTools diagnostics UI, each with a code, a fix, and a link to the migration guide. See extendServerRpc() and startSubprocess().

devtools:notify hook

Push a notification into the unified devframe Messages system — the same system that powers the Vite DevTools Messages dock and its toast overlay. The notification is forwarded to ctx.messages, so no direct kit access is needed, and calls made before the kit connects are buffered and replayed once it does:

// server-side (module setup)
nuxt.callHook('devtools:notify', {
  message: 'Something happened',
  level: 'error', // 'info' | 'warn' | 'error' | 'success' | 'debug' (default 'info')
  description: 'More details about what happened.',
  notify: true, // also show a transient toast
})

Omit autoDelete for a persistent, leveled entry that builds history in the Messages dock; set notify + autoDismiss + autoDelete for an ephemeral, toast-only notification. From the client, the same input is available on the injected client as client.devtools.notify(...).

@nuxt/devtools-kit/iframe-client

To provide complex interactions for your module integrations, we recommend to host your own view and display it in devtools via iframe.

To get the infomation from the devtools and the client app, you can do this in your client app:

import { useDevtoolsClient } from '@nuxt/devtools-kit/iframe-client'

export const devtoolsClient = useDevtoolsClient()

When the iframe been served with the same origin (CORS limitation), devtools will automatically inject __NUXT_DEVTOOLS__ to the iframe's window object. You can access it as a ref using useDevtoolsClient() utility.

onDevtoolsReady()

The client-side mirror of the server's onDevtoolsReady. The callback runs once the Vite DevTools client is connected and receives the DevToolsRpcClient (the same object as client.devtools.devtoolsKit), giving full devframe-native access. This is the recommended way to register client RPC functions:

import { onDevtoolsReady } from '@nuxt/devtools-kit/iframe-client'

onDevtoolsReady((kit) => {
  kit.client.register({
    name: 'my-module:on-update',
    type: 'event',
    handler: (payload) => {
      // ...
    },
  })
})

useDevtoolsClient()

It will return a ref of NuxtDevtoolsIframeClient object that are intially null and will be updated when the connection is ready.

NuxtDevtoolsIframeClient contains two properties:

  • host: APIs to communicate with the main app in browser
  • devtools: APIs to communicate with the devtools

host can be undefined when devtools are accessed standalone or from a different origin.

For example, you can get the router instance from the client app:

const router = computed(() => devtoolsClient.value?.host?.nuxt.vueApp.config.globalProperties?.$router)

The devtools object also exposes devtoolsKit — the connected Vite DevTools RPC client (mirroring nuxt.devtools.devtoolsKit on the server) — for full devframe-native access. Use it to register client RPC functions, call server functions, and more:

const kit = devtoolsClient.value?.devtools.devtoolsKit
kit?.client.register({
  name: 'my-module:on-update',
  type: 'event',
  handler: (payload) => {
    // ...
  },
})

This replaces the deprecated devtools.extendClientRpc().

onDevtoolsClientConnected()

Similiar to useDevtoolsClient() but as a callback style:

import { onDevtoolsClientConnected } from '@nuxt/devtools-kit/iframe-client'

onDevtoolsClientConnected(async (client) => {
  // client is NuxtDevtoolsIframeClient

  const config = client.devtools.rpc.getServerConfig()
  // ...
})

@nuxt/devtools-kit/host-client

When you have iframe for your devtools view, sometimes you need to communicate with the devtools host (the main app in browser) with a runtime plugin. You can use @nuxt/devtools-kit/host-client to do that.

useDevtoolsHostClient()

It will return a ref of NuxtDevtoolsHostClient object that are intially null and will be updated when the host is initialized by NuxtDevtools.

import { useDevtoolsHostClient } from '@nuxt/devtools-kit/host-client'

export default defineNuxtPlugin({
  name: 'my-module:devtools',
  setup(nuxtApp) {
    const devtoolsHost = useDevtoolsHostClient()

    // ...
  }
})

onDevtoolsHostClientConnected()

Similiar to useDevtoolsHostClient() but as a callback style:

import { onDevtoolsHostClientConnected } from '@nuxt/devtools-kit/host-client'

onDevtoolsHostClientConnected(async (host) => {

})