Nuxt framework logo and full-stack web platform concept
9 min readVue 3 Migration

Nuxt 2 to Nuxt 3: When "Just Upgrade Vue" Becomes a Full Platform Migration

A Vue 2 SFC app can follow @vue/compat and a clear router/store plan. A Nuxt 2 app drags server middleware, data fetching, module hooks, and deployment targets. “Upgrade Vue in place” is rarely the right mental model. Here is the platform map—and how Nuxt Bridge fits the gap.

What actually changes: build pipeline (Nitro), file-based routing conventions, async data APIs, plugin signatures, and often hosting ( SSR and hydration). Treat each as a workstream with owners—otherwise the team debates “one big PR” for six months.

1. Module compatibility matrix

Inventory @nuxtjs/* and community modules. Many have Nuxt 3–native successors; some require replacement (auth, PWA, i18n in particular). A greenfield roadmap session should list these before line one of the code port.

2. Data fetching and stores

Map Nuxt 2 asyncData/fetch to Nuxt 3 patterns (useAsyncData / useFetch). Pair with Pinia for client state. This layer is where subtle SSR bugs (double fetch, state leakage) appear.

3. When Bridge helps—and when it delays

Bridge can unblock incremental movement, but it is a phase with its own tax. Budget time or you trade a “Bridge forever” org chart problem for the EOL date.

4. Routing and layouts: convention deltas

File-based routing looks similar on the surface, but the conventions changed in ways that bite during the port:

ConcernNuxt 2Nuxt 3
Dynamic route_id.vue[id].vue
Catch-all_.vue[...slug].vue
Layoutslayout: 'admin' in component optionsdefinePageMeta({ layout: 'admin' })
MiddlewareGlobal or per-page stringdefineNuxtRouteMiddleware + definePageMeta
Error pagelayouts/error.vueerror.vue at app root

Vue Router itself jumps from 3 to 4 underneath, with its own breaking changes—see router 3 to 4. Layouts that overrode parent slots in v2 typically need a small refactor on Nuxt 3.

5. Plugins and module hooks

Nuxt 2 plugin signatures (({ app, store }, inject) => { ... }) move to defineNuxtPlugin:

// plugins/api.ts (Nuxt 3)
export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig()
  const api = $fetch.create({ baseURL: config.public.apiBase })

  return {
    provide: { api },
  }
})

// usage:
// const { $api } = useNuxtApp()

Two patterns deserve special attention. First, anything that injected into context.app (like custom error handlers) usually moves to a plugin or a app.config.ts entry. Second, server-only logic that ran in Nuxt 2 plugins must move to a Nitro server route or a server/ handler—Nuxt 3 keeps server and client plugins separated more strictly.

6. Hosting and deployment changes

Nitro decouples Nuxt from a specific Node server runtime. The same codebase can target Node, Vercel, Netlify, Cloudflare Workers, AWS Lambda, or static export. That flexibility is great—and a project risk if your ops team is not in the loop. Decide the deployment target before the cutover, not after.

Concrete migration questions to answer:

  • Are we keeping a long-running Node server, moving to serverless functions, or going edge?
  • What is the cold-start tolerance for our slowest route? (Edge runtimes have surprisingly different rules from Lambda.)
  • How do environment secrets flow into runtimeConfig? The Nuxt 2 pattern of reading process.env directly in components stops working at build time.
  • Do we still need a custom server/index.js, or can Nitro handlers replace it entirely?

If hosting answers are unclear, consider Nuxt Bridge as a phase that decouples the Vue 3 work from the runtime change.

7. Testing strategy during the port

Nuxt apps fail interestingly because so much can go subtly wrong on the SSR/hydration boundary. The tests that catch real regressions:

  • Hydration smoke test on the top 10 routes. Render server-side, then mount in a real browser and assert the DOM equals the SSR output. Mismatches usually reveal misuses of useState vs. component-local refs.
  • Data fetching contract tests. Confirm useAsyncData keys are stable across renders—forgetting the key argument causes double-fetching that only shows up under load.
  • Component tests for migrated UIs. Run them with Vue Test Utils and a couple of Cypress component sweeps for the trickiest forms.
  • Performance budget on first byte and LCP. Compare to your Nuxt 2 baseline. Don't trust dev-mode numbers—use field measurements.

8. Migration checklist for Nuxt teams

  • Inventory every @nuxtjs/* and community module; map to Nuxt 3 successor or replacement.
  • List all asyncData / fetch hooks; plan useAsyncData/useFetch equivalents with explicit keys.
  • Document every plugin's purpose; flag the ones that touch context.app or inject globals.
  • Pick a deployment target with the ops team in the room.
  • Decide on Pinia store layout before the first store is ported.
  • Lock in TypeScript strictness expectations early; Nuxt 3 is much more TS-native than Nuxt 2.
  • Write a short Bridge-or-not memo with named trade-offs for leadership.
  • Schedule a scope freeze window aligned with the deepest cutover work.

9. FAQ

Should we go Bridge or skip straight to Nuxt 3?

If your modules and hosting are ready, skip Bridge. If not, Bridge is a useful phase—but only when the Nuxt 3 cutover is funded too. The detailed decision criteria live in Nuxt Bridge: stepping stone or second migration.

Is Nuxt 3 always faster than Nuxt 2?

In dev (Vite), yes—dramatically. In production, Nitro is usually faster, especially on edge runtimes; but a misconfigured deployment can be slower than the Node app it replaced. Measure on real traffic.

Can we run Nuxt 2 and Nuxt 3 side by side?

Yes, behind a reverse proxy. The strangler fig pattern works for Nuxt as well as plain Vue, especially when one team is on marketing pages and another on the app shell.

How long does it really take?

For a medium SaaS with ~80 routes and a few custom modules, 12–20 focused engineering weeks is realistic. Smaller marketing sites can ship in 4–6 weeks; large platforms with custom Nuxt 2 modules take a quarter or more. See timeline framing.

Nuxt in production? We can scope it.

Platform migrations with honest timelines and module-by-module plans.

Book a Nuxt call

Conclusion

Nuxt 2 to 3 is an architecture project: server, build, and app layers move together. Name the workstreams, validate modules, and do not call it a “Vue 3 re-skin” in your steering deck—it is not one.

Related guides