Nuxt

@devframes/nuxt splits into @devframes/nuxt/single (author one devframe) and @devframes/nuxt/hub (mount a hub); the bare import throws.

@devframes/nuxt splits into @devframes/nuxt/single (author one devframe) and @devframes/nuxt/hub (mount a hub); the bare import throws.

The single module wires a Nuxt SPA as a devframe's browser side and types useNuxtApp().$rpc as DevframeRpcClient.

Install

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@devframes/nuxt/single'],
})

Using $rpc

app.vue
<script setup>
const { $rpc } = useNuxtApp()
const payload = await $rpc.call('my-tool:get-payload')
</script>

Or a composable:

composables/usePayload.ts
export function usePayload() {
  const { $rpc } = useNuxtApp()
  return useAsyncData('payload', () => $rpc.call('my-tool:get-payload'))
}

Options

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@devframes/nuxt/single'],
  devframe: {
    baseURL: './', // where the devframe snapshot lives, relative to the page
    skipAppDefaults: false, // opt out of the app.baseURL / vite.base defaults
  },
})
  • baseURL resolves against document.baseURI at runtime.
  • skipAppDefaults: true ships absolute asset paths.

Dev-time RPC bridge

Pass a devframe definition:

nuxt.config.ts
import myDevframe from './src/my-tool' // defineDevframe(...) export

export default defineNuxtConfig({
  modules: [['@devframes/nuxt/single', { devframe: myDevframe }]],
})

nuxt dev now:

  • Starts a WebSocket RPC server on a get-port-please port (respects devframe.cli.port / portRange / random).
  • Registers Vite middleware at ${baseURL}__connection.json.
  • Runs devframe.setup(ctx, { flags }).
  • Cleans up on Vite restart, nuxt dev shutdown, and bundle close.

On by default when devframe is set; disable it (browser side only) with devMiddleware: false.

Customizing the bridge

nuxt.config.ts
export default defineNuxtConfig({
  modules: [['@devframes/nuxt/single', {
    devframe: myDevframe,
    devMiddleware: {
      port: 7777,
      host: '0.0.0.0',
      flags: { config: process.env.MY_CONFIG },
    },
  }]],
})
  • port pins the bridge port (else get-port-please picks one).
  • host defaults to nuxt.options.devServer.host ?? devframe.cli?.host ?? 'localhost'.
  • flagsdevframe.setup(ctx, { flags }).

Relationship to createCac

Production uses createCac (or createBuild), producing a static __connection.json + __rpc-dump/ snapshot from clientAssets:

my-tool/
├── bin.mjs               # createCac(myDevframe).parse()
├── src/
│   ├── my-tool.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 clientAssets

How it works

At build time it sets the app.baseURL / vite.base defaults, merges { devframe: { baseURL } } into runtimeConfig.public, and injects a client-only plugin (helpers/nuxt/runtime/plugin.client):

const rpc = await connectDevframe({ baseURL: config.public.devframe.baseURL })
return { provide: { rpc } }

At runtime the SPA fetches ./__connection.json and branches on backend: websocket in dev, static from a createBuild snapshot.

Mounting a hub

@devframes/nuxt/hub mounts a whole hub alongside nuxt dev, injecting @devframes/hub-ui's dock. ui swaps the default; ui: false gives a headless hub via @devframes/nuxt/hub/client.

nuxt.config.ts
export default defineNuxtConfig({
  modules: [['@devframes/nuxt/hub', { devframes: [] }]],
})

Nuxt DevTools (@nuxt/devtools) integrates the same protocol natively; this module recommends it once (silence with { quiet: true }).

See also