Nuxt Helper
The @devframes/nuxt module wires a Nuxt-built SPA as a devframe client, and optionally serves the dev-time RPC bridge alongside nuxt dev. It runs inside the Nuxt app that consumes your devframe.
It handles the four things every Nuxt-powered standalone devtool needs:
- Base-agnostic assets. Sets
app.baseURL: './'andvite.base: './'so the same production build works at/,/tool/, and any other deployment path without build-time URL rewriting. - Runtime RPC connection. Adds a client plugin that calls
connectDevframe()once on page load and provides the result as$rpcon the Nuxt app. - Dev-time RPC bridge. When you pass
devframe,nuxt devspins up a separate WebSocket RPC server and serves__connection.jsonso the SPA can reach it — no hand-rolled Vite plugin required. - TypeScript augmentation.
useNuxtApp().$rpcis typed asDevToolsRpcClientout of the box.
Install
export default defineNuxtConfig({
modules: ['@devframes/nuxt'],
})That's it for the zero-config path. The module sets sane defaults for app.baseURL and vite.base, registers the client plugin, and exposes devframe/baseURL on useRuntimeConfig().public.
Using $rpc
<script setup>
const { $rpc } = useNuxtApp()
const payload = await $rpc.call('my-tool:get-payload')
</script>Or from a composable:
export function usePayload() {
const { $rpc } = useNuxtApp()
return useAsyncData('payload', () => $rpc.call('my-tool:get-payload'))
}Options
export default defineNuxtConfig({
modules: ['@devframes/nuxt'],
devframe: {
baseURL: './', // where the devframe snapshot lives, relative to the page
skipAppDefaults: false, // opt out of the app.baseURL / vite.base defaults
},
})baseURLdefaults to'./', which resolves againstdocument.baseURIat runtime. The connection meta and dump shards sit next toindex.html, so the same build works at any deployment path.skipAppDefaults: truedisables theapp.baseURL: './'/vite.base: './'defaults. Use this when you're shipping with absolute asset paths and have your own base-URL story.
Dev-time RPC bridge
Pass your devframe definition to wire nuxt dev up to the RPC backend:
import devframe from './src/devframe' // defineDevframe(...) export
export default defineNuxtConfig({
modules: [['@devframes/nuxt', { devframe }]],
})That's the full setup. Behind the scenes, nuxt dev now:
- Starts a separate WebSocket RPC server on a port resolved via
get-port-please(respectsdevframe.cli.port/portRange/random). - Registers Vite middleware at
${baseURL}__connection.jsonso the SPA reads it on load. - Runs
devframe.setup(ctx, { flags })once the bridge is up, registering your RPC functions. - Cleans up the bridge on Vite restart,
nuxt devshutdown, and bundle close.
The bridge is on by default whenever devframe is set. Skip it (back to client-only) with devMiddleware: false.
Customizing the bridge
export default defineNuxtConfig({
modules: [['@devframes/nuxt', {
devframe,
devMiddleware: {
port: 7777,
host: '0.0.0.0',
flags: { config: process.env.MY_CONFIG },
},
}]],
})portpins the bridge port. Skip it to letget-port-pleasepick a free port.hostcontrols the bridge bind host. Defaults tonuxt.options.devServer.host ?? devframe.cli?.host ?? 'localhost', sonuxt dev --hostpropagates automatically. Set this manually when your Nuxt server config doesn't surfacehost(e.g. custom listen options).flagsis forwarded todevframe.setup(ctx, { flags }). Use it to pass env-derived configuration into the RPC layer.
Relationship to createCli
The bridge handles the dev workflow. Production deploys still go through createCli (or createBuild), which produces a static __connection.json + __rpc-dump/ snapshot from cli.distDir:
my-tool/
├── bin.mjs # createCli(devframe).parse()
├── src/
│ ├── devframe.ts # defineDevframe + setup(ctx) { ctx.rpc.register(...) }
│ └── app/ # Nuxt SPA — uses `@devframes/nuxt`
└── dist/
├── cli.mjs # bundled Node entry
└── public/ # Nuxt build output, pointed at by cli.distDirIn dev (nuxt dev) the bridge is live. In production (<your-cli> build then <your-cli> spa) the SPA loads the static dump.
How it works
At build time the module:
- Sets
nuxt.options.app.baseURLto'./'(unless already set) - Sets
nuxt.options.vite.baseto'./'(unless already set) - Merges
{ devframe: { baseURL } }intoruntimeConfig.public - Injects a client-only plugin (
helpers/nuxt/runtime/plugin.client) that:tsconst rpc = await connectDevframe({ baseURL: config.public.devframe.baseURL }) return { provide: { rpc } }
At runtime the built SPA fetches ./__connection.json (resolved against document.baseURI) and branches on the backend field — websocket in dev, static from a createBuild snapshot.
See also
- Standalone CLI recipe — end-to-end walk-through
- Client —
connectDevframereference - Adapters — CLI / Vite / Build / SPA / Kit / Embedded / MCP