Let's look at how Nuxt DevTools v4 unifies the frontend ecosystem by moving to a framework-agnostic core called Devframe, and how you can migrate your modules.
What you'll learn:
- Why Nuxt decoupled its devtools from framework-specific code
- How Devframe lets you write a single tool that runs in Vite, Nuxt, CLI, or MCP servers
- How to update deprecated
@nuxt/devtools-kitAPIs to the newonDevtoolsReadycontext - Typing RPC communication with Standard Schema libraries like Valibot
The Architecture Shift: From Monolith to Layers
In older versions, every Nuxt module had to implement its own custom API to inject panels and server endpoints into the devtools. This meant duplicating work across Vite, Vue, and Nuxt.
Nuxt DevTools v4 fixes this by splitting the stack into three distinct layers. Each layer inherits capabilities from the layer beneath it.
┌───────────────────────────────────────────────┐
│ Nuxt DevTools v4 │ (Nuxt modules, Pinia, Auto-imports)
├───────────────────────────────────────────────┤
│ Vite DevTools │ (Vite plugins, Vitest, Rolldown)
├───────────────────────────────────────────────┤
│ Devframe Hub │ (Framework-agnostic Request/Response handler)
└───────────────────────────────────────────────┘
Speed Note: Because Vue DevTools and Vite DevTools are no longer vendored into Nuxt's custom codebase, bug fixes and feature updates flow downstream automatically without extra wrapper maintenance.
Migrating from @nuxt/devtools-kit
If your custom module relies on addCustomTab or extendServerRpc, it won't break immediately. Nuxt DevTools v4 ships with a transition shim that maps old helper functions to the new system while logging warnings (NDT_DEP_xxxx) in development.
Here is how you update your module setup to use the new onDevtoolsReady lifecycle hook:
import { defineNuxtModule } from '@nuxt/nuxt-kit'
import { onDevtoolsReady } from '@nuxt/devtools-kit'
export default defineNuxtModule({
setup() {
onDevtoolsReady((ctx) => {
// Register a panel dock using the new API
ctx.docks.register({
id: 'my-module',
title: 'My Module',
icon: 'carbon:apps',
type: 'iframe',
url: '/__my-module/',
})
// Scoped typed RPC handler
ctx.scope('my-module').rpc.register({
name: 'get-modules',
type: 'query',
handler: async () => {
return [{ id: 'core', size: 1024 }]
},
})
})
},
})
Gotcha: Vnode-based views and advanced tab categories are not fully mapped in the shim layer yet. If your module heavily relies on custom UI vnodes, keep the shim enabled until you refactor to an iframe or web component approach.
Going Portable with defineDevframe
The real power of Devframe is portability. Instead of writing a panel locked inside Nuxt, you can define your tool once and ship it to multiple targets: Vite plugins, static build reports, CLI tools, or even Model Context Protocol (MCP) servers for AI coding agents.
Here is how you define a portable devframe with typed RPC inputs validated via Valibot:
import { defineDevframe, defineRpcFunction } from 'devframe'
import * as v from 'valibot'
const getMetrics = defineRpcFunction({
name: 'get-metrics',
type: 'query',
args: [v.object({ threshold: v.number() })],
returns: v.array(v.object({ name: v.string(), load: v.number() })),
handler: async ({ threshold }) => {
// Fetch system metrics
return [{ name: 'bundle-size', load: 450 }].filter(m => m.load > threshold)
},
})
export default defineDevframe({
id: 'perf-inspector',
name: 'Perf Inspector',
icon: 'ph:gauge-duotone',
clientAssets: 'client/dist',
setup(ctx) {
ctx.scope('perf-inspector').rpc.register(getMetrics)
},
})
Bridging to Different Build Hosts
Once your devframe is defined, you create thin wrappers for your target environments using official adapters. This allows the exact same client UI and server logic to run inside a local Vite build, a command-line utility, or an AI agent workspace.
import { createPluginFromDevframe } from '@vitejs/devtools-kit/node'
import { createCac } from 'devframe/adapters/cac'
import { createMcpServer } from 'devframe/adapters/mcp'
import devframeDefinition from './devframe'
// 1. Works in Vite & Nuxt DevTools
export const vitePlugin = createPluginFromDevframe(devframeDefinition)
// 2. Runs as a standalone terminal CLI
export const runCli = () => createCac(devframeDefinition).parse()
// 3. Exposes tools to AI coding agents via MCP over stdio
export const startMcp = () => createMcpServer(devframeDefinition, { transport: 'stdio' })
Accessibility Note: When building custom devframe panels that render inside iframes or host environments, ensure keyboard focus states remain visible (
focus-visible:ring-2) so developers navigating via keyboard don't lose their place.
Summary
Nuxt DevTools v4 represents a major step forward for the JavaScript tooling ecosystem. By decoupling developer tools from framework specifics into Devframe, you write panels once and run them anywhere.
Next Steps:
- Upgrade your Nuxt project to Nuxt 5 or install
@nuxt/devtools@4on Nuxt 4.5+ - Audit your custom modules for deprecation warnings in your terminal console
- Explore the Devframe repository to build your first portable tool and test it with an MCP server