Nuxt rules
The full @geoql/oxlint-plugin-nuxt-doctor rule catalogue. ai-slop, data-fetching, hydration, server-routes.
The Nuxt plugin ships 10 rules across four categories. It is loaded
by @geoql/nuxt-doctor automatically; you do not need to register
it. The Vue plugin is also loaded — every Vue rule still applies.
ai-slop
Same idea as the Vue plugin: patterns that AI agents reach for because they look right in a chat, not because they work in Nuxt.
| rule id | severity | what it catches |
|---|---|---|
no-fetch-in-setup | warn | await fetch(...) at the top of <script setup> |
no-useState-for-server-data | error | useState() to hold data that came from the server |
no-explicit-imports-of-auto-imported | warn | importing things Nuxt already auto-imports |
no-process-client-server | warn | process.client / process.server (Nuxt 3 syntax) |
no-useState-for-server-data is the most-graded rule in the Nuxt
set. It catches the AI habit of dropping server payloads into
useState() so the template can react to them — except the data
was the server payload, so the reactivity is wrong by construction.
data-fetching
Nuxt's data primitives are deceptively similar. useFetch,
useAsyncData, $fetch, and useState all return refs, but only
two of them carry SSR-aware hydration. The doctor flags the cases
where the model picked the wrong one.
| rule id | severity | what it catches |
|---|---|---|
useAsyncData-key-required-in-loop | warn | useAsyncData() inside a v-for without a key |
The key collision in a loop is the bug — every iteration of the
v-for would otherwise share the same payload, and the last
iteration wins. The fix is always to derive a stable key from
the loop variable.
hydration
Hydration mismatches are the AI-agent's favorite way to ship a "works locally, breaks in production" bug. The doctor is strict about anything that touches the DOM during SSR.
| rule id | severity | what it catches |
|---|---|---|
no-document-in-setup | error | document.* access inside <script setup> |
clientOnly-for-browser-apis | warn | window.*, localStorage.*, etc. without <ClientOnly> |
no-document-in-setup is non-negotiable. document does not exist
on the server, and the failure mode is a ReferenceError that
crashes the entire SSR pass. The doctor emits this at error so it
gates the default --fail-on error CI run.
server-routes
Nuxt's server/api/ directory is the easiest place for AI agents
to introduce subtle security holes. The doctor flags them.
| rule id | severity | what it catches |
|---|---|---|
validate-body-with-h3-v2 | warn | readBody() without a Zod schema or validator |
defineEventHandler-typed | warn | defineEventHandler() without an H3EventContext return type |
createError-on-failure | warn | bare throw new Error() in a server route |
validate-body-with-h3-v2 is the rule you want running on every
PR. An unvalidated request body is the single most common way a
prompt-injection attack reaches your database.
Server-route rules look at the request/response shape, not just
syntactic correctness. The doctor needs the Nuxt context to know
that a file at server/api/users.post.ts is a POST handler — it
reads nuxt.config.ts and the directory structure to build that
context. Make sure your project has a real nuxt.config.ts and
the doctor will pick up the routes automatically.
Reading the rule list at runtime
You can dump the entire rule catalogue from the CLI:
npx -y @geoql/nuxt-doctor list-rules
To filter:
npx -y @geoql/nuxt-doctor list-rules --source oxlint
npx -y @geoql/nuxt-doctor list-rules --category hydration
npx -y @geoql/nuxt-doctor list-rules --json
To inspect a single rule:
npx -y @geoql/nuxt-doctor explain no-useState-for-server-data
This prints the rule's description, severity, category, the source plugin, and the recommended fix.
What's not here
- Build config validation (
nuxt.config.tscorrectness) — the doctor readsnuxt.config.tsfor context but does not flag bad configuration. Usenuxt typecheckfor that. - Module version mismatches — out of scope; use
nuxi doctoror a real Renovate setup. - Performance benchmarks for server routes — the doctor is static; it does not run your handlers.