Nuxt 4 development recipes with AI agents
PLNuxt development recipes for AI agents rest on three moves: pin the agent to Nuxt 4 documentation through the official Nuxt MCP server, because agents mix Nuxt 3 and unreleased Nuxt 5 APIs; write the Nuxt 4 conventions into the agent’s instruction file; and let nuxt typecheck, @nuxt/test-utils and nuxt build prove each change. The recipes target Nuxt 4.5.
This page is for developers who let Claude Code, Codex or Cursor build a full-stack Nuxt app, and for the tech lead who has to trust the result. The agent’s feature works on your machine, but it put the pages in pages/ instead of app/pages/, passed a Zod schema straight to readValidatedBody, and read document.cookie in a plugin that also runs on the server. The page works after client-side navigation, the hydration warnings scroll past, and the first user who opens a deep link directly gets a 500.
What these Nuxt recipes give you
Section titled “What these Nuxt recipes give you”- A context setup that keeps the agent on Nuxt 4 when both its training data (Nuxt 3) and the newest docs and skills (Nuxt 5) pull it elsewhere
- The official Nuxt MCP server wired into all three tools, plus the experimental dev-server MCP module
- Seven prompts for the Nuxt work agents most often get wrong: validated server routes, data fetching, sessions, hybrid rendering, Cloudflare Workers, Nuxt Content, and the test harness
- A gate table that signs a change off without anyone reading every line, and a failure table with recovery steps
Keep the agent on Nuxt 4 docs
Section titled “Keep the agent on Nuxt 4 docs”An agent writing Nuxt code draws on three sources that disagree. Its training data is mostly Nuxt 3, with pages/ at the root and data that defaults to null. The newest material describes Nuxt 5: Nitro v3, h3 v2, and server helpers imported from nuxt/server, which only arrives in Nuxt 4.6. Your package.json says 4.5. Every step below exists to make the third source win.
-
Write the Nuxt 4 rules down where every tool finds them: in
AGENTS.mdfor Codex, inCLAUDE.mdfor Claude Code (or aCLAUDE.mdcontaining@AGENTS.md; from v2.1.277 Claude Code readsAGENTS.mdwhen there is noCLAUDE.md), and in a project Rule for Cursor.## Nuxt rules for this repo (Nuxt 4.5, nitropack 2, h3 1.x)- Vue code lives in app/ (app/pages, app/components, app/composables, app/middleware,app/plugins). server/, shared/, content/, public/ and nuxt.config.ts stay at the root.- Code used by both app and server goes in shared/utils or shared/types.- Look up APIs in the Nuxt 4.x docs (Nuxt MCP server, version 4.x). Never apply Nuxt 5guidance: no h3 v2 APIs, and no nuxt/server imports until nuxt >= 4.6.- createError takes { status, statusText } (statusCode/statusMessage are deprecated).- Never add h3 or nitropack to package.json; use the versions Nuxt brings.- readValidatedBody / getValidatedQuery take a function: readValidatedBody(event, Schema.parse).- useFetch/useAsyncData: data and error start as undefined; branch on status, not pending.- No browser globals (window, document, localStorage) in setup, plugins or middlewareunless guarded by import.meta.client or inside onMounted.- Done means: npx nuxt typecheck, npx eslint ., npx vitest run and npx nuxt build pass. -
Connect the Nuxt MCP server (next section) and tell the agent to query it with the
4.xversion filter. The server’slist-documentation-pagestool accepts3.x,4.x,5.xorall, so an unfiltered lookup can return Nuxt 5 pages. The filter is not a version pin: the 4.x docs already describe Nuxt 4.6 features such asnuxt/server, so check thenuxtversion inpackage.jsonbefore accepting code built on them. -
Check any Nuxt skill before you install it. The
nuxtskill inantfu/skillsstates that it “is based on Nuxt 5.x (5.0.0-0), generated at 2026-09-25”. On a Nuxt 4.5 project it teachesnuxt/serverimports and h3 v2, so leave it out until you upgrade. Thenuxt-uiskill innuxt/uitargets@nuxt/uiv4 and is safe to add if you use that library; see installing and managing skills. -
Put the stale patterns in front of the reviewer too. These are the APIs the agent reaches for first:
Agent writes Status on Nuxt 4.5 Write instead pages/,components/,composables/at the root of a new projectNuxt 3 layout; Nuxt 4 defaults srcDirtoapp/app/pages/and so on; migrate an old app withnpx codemod@latest nuxt/4/file-structurereadValidatedBody(event, BodySchema)h3 1.x expects a validation function readValidatedBody(event, BodySchema.parse)if (data.value === null)orpending.valueas the loading flagdataanderrordefault toundefinedin Nuxt 4status.value === 'pending'anddata.value === undefinedimport { defineEventHandler } from 'nuxt/server'Arrives in Nuxt 4.6 (not exported by 4.5.x) The auto-imported defineEventHandlerrouteRules: { '/blog/**': { isr: 3600 } }on Cloudflare or Nodeisrreaches the CDN only on Netlify and Vercelswr: 3600everywhere elsepreset: 'cloudflare-pages'for a new WorkerPages preset preset: 'cloudflare_module', the preset Nitro recommends for Workers
Connect the agent to the Nuxt docs and your dev server
Section titled “Connect the agent to the Nuxt docs and your dev server”The Nuxt team runs a documentation MCP server at https://nuxt.com/mcp over HTTP, with no authentication. Its tools list and fetch documentation pages, blog posts, deployment guides, modules (with README, compatibility and download stats) and the changelog. The X-MCP-Tools header limits which tools load, which keeps the context cost down; see reducing MCP token cost.
# Terminal, in the project root. -s project writes .mcp.json so the team shares it.claude mcp add -s project --transport http nuxt https://nuxt.com/mcp \ -H "X-MCP-Tools: list-documentation-pages,get-documentation-page,get-module"Run /mcp in a session to confirm nuxt is connected.
# Terminal. Writes the server to ~/.codex/config.toml.codex mcp add nuxt --url https://nuxt.com/mcpRun codex mcp list to confirm the entry. codex mcp add has no header flag (checked in 0.157.1), so add the header to the entry it wrote, under [mcp_servers.nuxt]:
[mcp_servers.nuxt]url = "https://nuxt.com/mcp"http_headers = { "X-MCP-Tools" = "list-documentation-pages,get-documentation-page,get-module" }Add the server to .cursor/mcp.json in the project root. This is the shape the Nuxt docs give for Cursor.
{ "mcpServers": { "nuxt": { "type": "http", "url": "https://nuxt.com/mcp", "headers": { "X-MCP-Tools": "list-documentation-pages,get-documentation-page,get-module" } } }}The docs server knows Nuxt; it does not know your app. For that, Anthony Fu’s nuxt-mcp-dev module (marked “Experimental. Use with caution.”) adds an MCP endpoint to your dev server at http://localhost:3000/__mcp/sse and, per its README, updates the MCP config of VS Code, Cursor, Windsurf and Claude Code for you. Add it with modules: ['nuxt-mcp-dev'] in nuxt.config.ts. The endpoint uses the SSE transport, which Claude Code still supports but has deprecated, so treat it as a convenience rather than a gate. For the browser’s view of a page, add Playwright MCP; the other dev-server servers are compared in framework devtools MCP servers.
Validate a server route with Zod and a session check
Section titled “Validate a server route with Zod and a session check”Server routes are where the agent’s Nuxt 3 and Nuxt 5 habits collide. The route below needs a validated body, an authenticated user, and a proper status code for each failure.
The 400 on invalid input comes from h3 itself: when the validation function throws, readValidatedBody turns it into a 400. That is why passing the schema object is dangerous. The call fails on every request, and a test that only checks “some error” still passes.
Fetch data without duplicate requests or hydration errors
Section titled “Fetch data without duplicate requests or hydration errors”The agent’s usual data layer is $fetch inside onMounted, which fetches twice (once on the server, once on the client) or not at all on the server. The Nuxt pattern is useFetch or useAsyncData in setup, with a stable key and the status ref.
The shared-key assertion matters. In Nuxt 4, components that call useAsyncData or useFetch with the same key share one data, error and status ref, and the docs require consistent options across those calls. A composable that builds keys from unstable values (a new object each render) breaks the sharing without breaking a type check.
Add sessions and route middleware without SSR leaks
Section titled “Add sessions and route middleware without SSR leaks”Auth spans a server route, a route middleware, a plugin and a composable. The SSR boundary is where the agent fails: a plugin that reads document.cookie crashes the server render, and a middleware that trusts client state lets a crafted request through. nuxt-auth-utils keeps the session in a sealed cookie and gives you requireUserSession on the server and useUserSession in the app, which removes most of the hand-written token code.
Start in plan mode (/plan) and approve the file list before any code exists. After the change, run /code-review and ask it to list every file under server/api/ that does not call requireUserSession. For CI, run the same audit read-only: claude -p "…" --allowedTools "Read,Grep,Glob".
Run the change in its own worktree so a half-written plugin never breaks your dev server: codex exec --worktree --sandbox workspace-write "…". Then ask a second, read-only session (codex exec --sandbox read-only) for the list of unprotected server routes.
Use Plan Mode for the file list, then review the diffs of app/middleware/auth.ts and the login route in the editor. Those two files hold every decision. Search the diff for document. and window. before you accept it.
Choose a rendering mode per route
Section titled “Choose a rendering mode per route”Marketing pages should be static, the dashboard rendered on the client or per request, and the blog cached. In Nuxt, that is one routeRules block, and the one fact the agent gets wrong is which rules your host honours.
On Vercel or Netlify, swap swr for isr and state the host in the prompt. Nitro maps route rules to those platforms’ native CDN rules, and nowhere else.
Deploy to Cloudflare Workers with D1 and KV
Section titled “Deploy to Cloudflare Workers with D1 and KV”Nitro’s cloudflare_module preset builds a Workers module, and cloudflare.deployConfig: true generates the wrangler.json. Bindings reach your server code through event.context.cloudflare.env. The agent’s two classic mistakes are the Pages preset and a binding name collision with Nuxt Content, which uses a D1 binding named DB by default.
For the Workers platform itself (Queues, Durable Objects, limits), see serverless development patterns; for letting the agent inspect your Cloudflare account, see the Cloudflare MCP server. Drizzle schema and migration patterns are in ORM best practices.
Build a blog with Nuxt Content 3
Section titled “Build a blog with Nuxt Content 3”Nuxt Content 3 replaced the v2 query API with collections declared in content.config.ts and queried with queryCollection(), backed by SQLite (D1 on Cloudflare). An agent trained on v2 writes queryContent() and <ContentDoc>, which no longer exist.
Set up the Nuxt test harness the agent runs every time
Section titled “Set up the Nuxt test harness the agent runs every time”Tests only protect you if the agent runs them after every edit. @nuxt/test-utils 4.x splits the work into Vitest projects: plain unit tests in Node, Nuxt-runtime tests (components, composables, plugins) in the nuxt environment, and end-to-end tests that build and serve the app.
Add a Stop hook whose script runs npx nuxt typecheck && npx vitest run and exits with code 2 on failure. The error goes back to the agent instead of ending the turn. See hooks automation.
Put the “Done means” line from the rules block in AGENTS.md and ask for the command output in the final message. For CI, codex exec --sandbox workspace-write -o result.md "…" writes that message to result.md, which your CI job can upload as an artifact.
Keep the “Done means” line in your project Rules and run the harness in the integrated terminal after each agent step. Paste a failing mountSuspended stack trace back into the chat instead of describing it.
The deliberate failure at the end is the cheapest check that the tests can fail at all. An agent-written test that passes whatever the code does is worse than none.
How to verify agent-written Nuxt code without reading every line
Section titled “How to verify agent-written Nuxt code without reading every line”Read the diffs of nuxt.config.ts, anything under app/middleware/ and app/plugins/, and every server route that reads a session. Let the gates prove the rest.
| Gate | Command or tool | What goes red |
|---|---|---|
| Types | npx nuxt typecheck | Wrong useFetch return types, a nuxt/server import on Nuxt 4.5, a shared type the client did not follow |
| Lint | npx eslint . with @nuxt/eslint 1.17.0 | Unused auto-imports, browser globals where you ban them with no-restricted-globals |
| Unit and runtime tests | npx vitest run --project unit --project nuxt | Composable behaviour, component states, mocked endpoints |
| End to end | npx vitest run --project e2e, or Playwright against npx nuxt preview | Auth bypass, status codes, cache headers, redirects |
| Build | npx nuxt build | Prerender errors, Nuxt Content parse errors, preset problems |
| Hydration | Playwright test that fails on any console message containing Hydration | Server and client markup that differ |
| Bundle | npx nuxt analyze (experimental) | A dependency that doubled the client bundle |
The developer who owns the pull request signs off on the gate results and the files named above; CI enforces the rest. For how that review scales across a team, see reviewing agent pull requests, and for writing the acceptance criteria your e2e tests encode, see acceptance criteria agents can verify.
When agent-written Nuxt code breaks
Section titled “When agent-written Nuxt code breaks”| Symptom | Cause | Recovery |
|---|---|---|
| Every POST to a new route returns 400 | readValidatedBody(event, Schema) with the schema object | Pass Schema.parse; add a test that a valid body returns 2xx, not only that invalid ones fail |
| Pages 404 after the agent adds them | Files created in root pages/ while the app uses app/ (or the reverse) | Move them under app/pages/; keep the directory rule in AGENTS.md |
Cannot find module 'nuxt/server' or h3 errors about event.req | Guidance for Nuxt 4.6+ or Nuxt 5 (the 4.x docs already describe 4.6; the docs main branch and Nuxt 5 skills describe 5) | Check package.json’s nuxt version before accepting nuxt/server imports; on 4.5, remove any direct h3 dependency and revert to auto-imports |
document is not defined during SSR | Browser API in a plugin, middleware or setup | Guard with import.meta.client, move it into onMounted, or name the plugin *.client.ts |
| Hydration mismatch warnings | Date.now(), Math.random() or locale formatting in render | Compute on the server and pass it through useState, or wrap the widget in <ClientOnly> |
| A page shows another request’s data | Two useFetch calls share a key with different options | Build keys from the full URL and query; keep options identical for a shared key |
Blog pages never refresh after isr: true | Without a TTL, isr: true keeps a page until the next deploy; without Netlify or Vercel, isr behaves like swr and gets no CDN caching | Use swr with a TTL; add the cache-header e2e test |
| D1 queries hit the wrong database after adding Nuxt Content | Both use the DB binding | Rename your app binding (for example APP_DB) and redeploy |
Where to go next with Nuxt and agents
Section titled “Where to go next with Nuxt and agents”- Start from the frontend recipes overview for the browser and gate setup every framework recipe assumes.
- Take component-level and Pinia patterns further with Vue.js workflow recipes.
- Build the browser suite behind the e2e gate with end-to-end testing with agents.
- Compare Nuxt with the React stack in Next.js optimization recipes.
- Find other skills for Vue and Nuxt work in best skills for frontend development, and give agents current docs for the rest of your stack with Context7.
Did this guide help you?
One tap, no sign-in.
What will you do with it?
How did it read?
What got in the way? Pick any that apply
Your role?
Thanks. I read every answer. Michał Jaskólski
Did this guide help?
One tap each. Close whenever you like.
no sign-in