Next
@devframes/next's API is still settling; expect changes before a stable release.@devframes/next hosts devframes from a Next.js App Router app via a route handler: one fetch handler serves each SPA and its __connection.json via serveStaticHandler.
It splits into @devframes/next/single and @devframes/next/hub; the bare import throws. single gives withDevframe(), createDevframeNextHandler(), and a React RPC-client helper (@devframes/next/single/client).
Config
import { withDevframe } from '@devframes/next/single'
export default withDevframe({
// ...your own Next config
})withDevframe sets skipTrailingSlashRedirect: true, keeping the rest.
Hosting a single devframe
createDevframeNextHandler(definition) serves the SPA and starts a side-car RPC/WS server:
import { createDevframeNextHandler } from '@devframes/next/single'
import myDevframe from '@/my-tool'
export const runtime = 'nodejs'
export const dynamic = 'force-dynamic'
const handler = createDevframeNextHandler(myDevframe)
export const GET = handler.fetchclose() stops the side-car; ready resolves when listening.
| Option | Default | Description |
|---|---|---|
base | def.basePath ?? '/__<id>/' | SPA mount path. |
host | def.cli?.host ?? 'localhost' | Side-car bind host. |
port | from def.cli?.port | Side-car port. |
flags | none | Passed to def.setup(ctx, { flags }). |
auth | false | true for the OTP gate, or a handler. |
mcp | 'auto' | Expose the MCP route. 'auto' mounts once agent tools exist; true forces the origin-only route on (trusts same-machine callers); McpRouteOptions can add an authorization identity check. |
allowedOrigins | loopback-only | Widen the side-car WS origin check for a remotely-accessed dev server (container / Codespace / tunnel): extra origins, a WsOriginRegistry, or false to disable (the auth gate stays the trust boundary). |
key | @devframes/next:<id>:<base> | globalThis memoization key. |
Hosting a hub
@devframes/hub's initHub mounts every devframe under <base><id>/ behind one handler (memoize on globalThis; see examples/custom-hub-next):
import { DEVFRAMES_HUB_BASE, initHub } from '@devframes/hub/initiate'
// Next route handlers can't accept WS upgrades, so the socket asks for a
// side-car of its own; the browser discovers it via `__connection.json`.
const hub = initHub({
base: DEVFRAMES_HUB_BASE,
devframes: [myDevframe],
ws: { sidecar: true },
auth: false,
})
export const { handler } = hub // mount on a `[[...path]]` route handlerexport const runtime = 'nodejs'
export const dynamic = 'force-dynamic'
export async function GET(request: Request): Promise<Response> {
return handler(request) // serves every mounted SPA + connection meta
}React RPC client
'use client'
import { RpcProvider } from '@devframes/next/single/client'
export function Providers({ children }: { children: React.ReactNode }) {
return <RpcProvider baseURL="/__my-tool/">{children}</RpcProvider>
}useRpc() returns the DevframeRpcClient (null while connecting), useRpcStatus() the live { status, error }; both throw outside <RpcProvider>.
'use client'
import { useRpc, useRpcStatus } from '@devframes/next/single/client'
export function Panel() {
const rpc = useRpc()?.scope('my-tool:')
const { error } = useRpcStatus()
if (!rpc)
return <p>{error ? `connection failed: ${error.message}` : 'connecting…'}</p>
// rpc.rpc.call('get-payload'), rpc.sharedState, …
}Runtime
Handlers calling fetch pin runtime = 'nodejs' (Node side-car).
Mounting a hub
@devframes/next/hub's nextDevframeHub() is a globalThis-memoized handle; createNextDevframeHub() is the builder. UI defaults to @devframes/hub-ui; ui swaps it, ui: false gives a headless hub via @devframes/next/hub/client's useDevframeHubClient().
import { nextDevframeHub } from '@devframes/next/hub'
export const runtime = 'nodejs'
export const dynamic = 'force-dynamic'
const hub = nextDevframeHub({ devframes: [] })
export const GET = (req: Request) => hub.handler(req)
export const POST = (req: Request) => hub.handler(req)
export const DELETE = (req: Request) => hub.handler(req)The aggregate MCP route mounts by default once any mounted devframe exposes agent tools (the 'auto' setting). Force it on with mcp: true (origin-only, trusting same-machine callers), off with mcp: false, or add mcp: { authorization } for an identity check when the app is reachable beyond localhost.
No native hub UI provider here, so this scope stays quiet; createDevframeNextHost() is the low-level DevframeHost.
See also
Nuxt
@devframes/nuxt splits into @devframes/nuxt/single (author one devframe) and @devframes/nuxt/hub (mount a hub); the bare import throws.
Add-ons
Ready-to-run packages built on Devframe: built-in devframes you can run, compose, or learn from, and wire services that share one node-side capability across every devframe on a host.