Nuxt
Framework
A module plus a request-scoped runtime plugin for server rendering.
- Package
- @queryweave/nuxt
- Runtime
- Nuxt server and client
- Depends on
- @queryweave/core, @queryweave/vue, @queryweave/vue-router
- Requires
- nuxt, vue, vue-router
The Nuxt package does two separable things: it configures your application at build time, and it creates one adapter per Vue application instance at runtime. Those live in different entry points because they run at different times.
Install
Section titled “Install”pnpm add @queryweave/core @queryweave/vue @queryweave/nuxtexport default defineNuxtConfig({ modules: ["@queryweave/nuxt"],});Requires Nuxt 4.0 or newer on the 4.x line. The module declares the same range as the package’s peer dependency.
Example
Section titled “Example”<script setup lang="ts">import { defineQueryModel, param } from "@queryweave/core";
const products = defineQueryModel({ search: param.text().optional(), page: param.integer({ min: 1 }).default(1),});
// useQueryModel is auto-imported by the module.const filters = useQueryModel(products);const search = filters.field("search", { navigation: "replace" });</script>
<template> <input v-model="search" type="search" /> <p>Page {{ filters.values.page }}</p></template>No adapter is provided by hand. The module registers a plugin that installs one for you, and the Vue binding finds it through injection.
Module options
Section titled “Module options”export default defineNuxtConfig({ modules: ["@queryweave/nuxt"], queryweave: { autoImports: true, // register useQueryModel and provideQueryAdapter — default true enabled: true, // register the runtime plugin at all — default true },});Setting enabled: false disables the module entirely, which is useful when you want to install an
adapter yourself.
Request scoping
Section titled “Request scoping”This is the part that matters for server rendering.
Nuxt creates a fresh Vue application for every server-rendered request. The QueryWeave plugin creates its adapter inside that application’s setup and installs it on that instance:
// packages/nuxt/src/runtime/plugin.ts, in essenceconst adapter = createNuxtQueryAdapter(nuxtApp.$router, { server: import.meta.server });installQueryAdapter(nuxtApp.vueApp, adapter);Nothing is stored at module scope. A server handling two requests concurrently therefore has two adapters, each bound to its own router, and neither can observe the other.
On the server, update() and the other transitions resolve with outcome: "refused" and an
Error as reason rather than moving a router whose response is already being written. Reading
works as usual. A server render that must change the URL redirects with navigateTo().
SSR and hydration
Section titled “SSR and hydration”The adapter is backed by Vue Router, which Nuxt initializes from the request URL on the server and
from location on the client. Both sides therefore decode the same query with the same model, and
the first client render matches the server’s.
There is no state-transfer payload for query state, and none is needed: the URL is the transfer.
The exception is a model with an asynchronous refinement: its first snapshot is pending on both
sides. Await filters.settled() in the component — for example at the top level of
<script setup>, which Nuxt wraps in Suspense — when the server render must carry the settled
value.
History1 / 1
- SSR initial query
- Vue Router runtime
- hydration-safe state
One adapter per Vue application instance, which on the server means one per request.
Type a query, press Enter.
Typed state
{
"page": 1,
"sort": "created_at",
"status": "all"
}Canonical URL
Valid/productsRuntime exports
Section titled “Runtime exports”For applications that want to wire things themselves:
import { createNuxtQueryAdapter, installQueryAdapter } from "@queryweave/nuxt/runtime";import type { Router } from "vue-router";
export default defineNuxtPlugin((nuxtApp) => { const adapter = createNuxtQueryAdapter(nuxtApp.$router as Router, { server: import.meta.server }); installQueryAdapter(nuxtApp.vueApp, adapter);});installQueryAdapter accepts any QueryAdapter, so an application can substitute its own — a
memory adapter in a test, for example — without leaving the module.
The plugin also exposes the adapter as $queryWeaveAdapter on the Nuxt app, for code that needs it
outside a component.
Edge cases
Section titled “Edge cases”useQueryModeloutside a component still finds the adapter wherever Nuxt provides an injection context — a plugin, route middleware, or auseAsyncDatahandler. Somewhere it does not, pass$queryWeaveAdapteror a runtime explicitly.- A page that unmounts does not take the adapter’s route watcher with it. The next page’s binding keeps receiving navigation.
- Static generation decodes the query at request time in the browser; a pre-rendered page has no query of its own.
- Disabling auto-imports means importing
useQueryModelfrom@queryweave/vueyourself; the behavior is identical. isolatedDeclarationsis off for this package alone, because the Nuxt module type cannot be expressed without it. This is recorded in ADR 0007.