# Nuxt 4 in 2026: New Directory Structure and Migration from Nuxt 3
> Complete guide to Nuxt 4 directory structure, migration from Nuxt 3, data fetching changes, and TypeScript improvements. Step-by-step tutorial with code examples.
- Published: 2026-05-01
- Updated: 2026-05-01
- Author: SharpSkill
- Tags: nuxt, vue, migration, typescript, tutorial
- Reading time: 10 min
---
Nuxt 4 introduces a redesigned directory structure that separates application code from configuration, along with a singleton data fetching layer, shallow reactivity defaults, and split TypeScript contexts. Released in July 2025 and now at version 4.4, this major release focuses on evolution rather than revolution — making the migration path from Nuxt 3 significantly smoother than the Nuxt 2 to 3 jump.
> **Automated Migration Available**
>
> The Nuxt team partnered with Codemod to automate most migration steps. Run `npx codemod@latest nuxt/4/migration-recipe` to handle directory restructuring, data fetching updates, and deprecated API replacements automatically.
## The New app/ Directory Structure in Nuxt 4
Nuxt 4 moves all application source code into an `app/` directory by default. This separation solves a real problem: file watchers on Linux and Windows perform significantly better when application code lives in a dedicated subdirectory rather than mixed with `node_modules/`, `.git/`, and configuration files.
The new layout follows this structure:
```text
my-nuxt-app/
├─ app/
│ ├─ assets/
│ ├─ components/
│ ├─ composables/
│ ├─ layouts/
│ ├─ middleware/
│ ├─ pages/
│ ├─ plugins/
│ ├─ utils/
│ ├─ app.vue
│ ├─ app.config.ts
│ └─ error.vue
├─ content/
├─ public/
├─ shared/ # New: code shared between app and server
├─ server/
└─ nuxt.config.ts
```
The `shared/` directory is a notable addition. Any composable or utility placed in `shared/` becomes auto-imported in both the Vue app and the Nitro server, eliminating the need for manual imports when sharing validation schemas, type definitions, or utility functions across contexts.
## Step-by-Step Migration from Nuxt 3 to Nuxt 4
The upgrade process starts with a single command. Nuxt detects the existing flat structure and continues working without any changes, so the migration can happen incrementally.
```bash
# Upgrade Nuxt and deduplicate dependencies
npx nuxt upgrade --dedupe
```
After upgrading the package, move application files into the `app/` directory:
```bash
# Automate the directory restructuring
npx codemod@latest nuxt/4/file-structure
```
This codemod moves `assets/`, `components/`, `composables/`, `layouts/`, `middleware/`, `pages/`, `plugins/`, `utils/`, `app.vue`, `error.vue`, and `app.config.ts` into `app/`. Files that belong at the root — `nuxt.config.ts`, `server/`, `public/`, and `content/` — stay in place.
For projects that need to delay the restructuring, set the source directory explicitly:
```typescript
// nuxt.config.ts
export default defineNuxtConfig({
srcDir: '.',
dir: { app: 'app' },
})
```
This configuration tells Nuxt 4 to resolve files from the project root, matching the Nuxt 3 behavior exactly.
## Singleton Data Fetching Layer and Reactive Keys
Nuxt 4 fundamentally changes how `useAsyncData` and `useFetch` manage data. Multiple components calling the same key now share a single reactive reference instead of maintaining independent copies.
```typescript
// app/composables/useProductData.ts
export function useProductData(productId: string) {
return useAsyncData(
`product-${productId}`,
() => $fetch(`/api/products/${productId}`),
{
getCachedData: (key, nuxtApp, ctx) => {
// ctx.cause tells why the fetch is happening
if (ctx.cause === 'refresh:manual') return undefined
return nuxtApp.payload.data[key]
},
},
)
}
```
Three changes stand out in this new data layer:
- **Shared refs**: calling `useProductData('abc')` in two components returns the same `data`, `error`, and `status` refs. Updating one updates both.
- **Automatic cleanup**: when the last component using a key unmounts, Nuxt frees the associated data from memory.
- **Reactive keys**: wrapping a key in a computed or ref triggers automatic refetching when the value changes.
The `getCachedData` callback now receives a context object with a `cause` property (`'initial'`, `'refresh:hook'`, `'refresh:manual'`, or `'watch'`), enabling fine-grained control over when to serve cached data versus fetching fresh data.
> **Default Values Changed**
>
> Data and error from `useAsyncData`/`useFetch` now default to `undefined` instead of `null`. Update any `=== null` checks to `=== undefined` or use a loose equality check.
## Shallow Reactivity by Default for Better Performance
Nuxt 4 switches `data` from `useAsyncData` and `useFetch` to `shallowRef` instead of `ref`. Vue no longer recursively tracks every nested property, which delivers measurable performance improvements for API responses with deeply nested objects or large arrays.
```typescript
// app/pages/dashboard.vue
```
For most read-only data displays (dashboards, product listings, article pages), shallow reactivity works without any code changes. The `deep: true` option remains available for forms or interactive UIs that mutate nested properties directly.
## TypeScript Context Splitting and Improved Type Safety
Nuxt 4 generates separate TypeScript configurations for each context in the project:
- `.nuxt/tsconfig.app.json` — Vue application code
- `.nuxt/tsconfig.server.json` — Nitro server code
- `.nuxt/tsconfig.shared.json` — Shared utilities
- `.nuxt/tsconfig.node.json` — Build-time configuration
This separation means the IDE no longer suggests server-only APIs in client code, and vice versa. A single `tsconfig.json` at the project root references all four configs:
```json
{
"files": [],
"references": [
{ "path": "./.nuxt/tsconfig.app.json" },
{ "path": "./.nuxt/tsconfig.server.json" },
{ "path": "./.nuxt/tsconfig.shared.json" },
{ "path": "./.nuxt/tsconfig.node.json" }
]
}
```
Type checking in CI also changes. The `vue-tsc` command now requires the `-b` flag (build mode) to process project references correctly:
```bash
# Before (Nuxt 3)
nuxt prepare && vue-tsc --noEmit
# After (Nuxt 4)
nuxt prepare && vue-tsc -b --noEmit
```
Another TypeScript change: `compilerOptions.noUncheckedIndexedAccess` is `true` by default. Accessing an array element or object property by index now returns `T | undefined`, catching potential runtime errors at compile time.
## Normalized Component Names and Vue Router v5
Nuxt 4.3 upgraded to Vue Router v5, removing the dependency on `unplugin-vue-router`. For most applications, this upgrade is transparent.
Component naming conventions are now standardized. A component at `components/dashboard/MetricsCard.vue` gets the name `DashboardMetricsCard` consistently across all contexts — including ``, Vue DevTools, and test utilities.
```vue
```
Projects using `` with component name filters need to update the names to match this new convention. The previous behavior, where the name could vary depending on context, no longer applies.
## Handling Breaking Changes in Head Management
Nuxt 4 ships with Unhead v2, which removes several deprecated properties from `useHead` and `useSeoMeta`:
```typescript
// app/pages/product/[id].vue
```
For projects relying on template parameters or alias sorting, install these as explicit plugins:
```typescript
// app/plugins/unhead.ts
import { TemplateParamsPlugin, AliasSortingPlugin } from '@unhead/vue/plugins'
export default defineNuxtPlugin({
setup() {
const unhead = injectHead()
unhead.use(TemplateParamsPlugin)
unhead.use(AliasSortingPlugin)
},
})
```
> **Nuxt 3 Support Timeline**
>
> Nuxt 3 continues to receive security updates and critical bug fixes until July 31, 2026. After that date, Nuxt 3 becomes unsupported. Planning the migration now avoids running production applications on an EOL framework.
## Migration Checklist and Common Pitfalls
The automated codemod handles most changes, but several items require manual attention:
- **`window.__NUXT__` removal**: replace with `useNuxtApp().payload`. This global object is deleted after hydration in Nuxt 4.
- **`pages:extend` hook**: switch to the new `pages:resolved` hook, which runs after page meta scanning.
- **`dedupe` boolean**: replace `refresh({ dedupe: true })` with `refresh({ dedupe: 'cancel' })` and `false` with `'defer'`.
- **Inline styles**: only Vue component styles are inlined by default; global CSS loads as separate files. Add `features: { inlineStyles: true }` to restore the Nuxt 3 behavior.
- **`clearNuxtState`**: now resets to initial values instead of `undefined`. Use `clearNuxtState('key', { reset: false })` for the old behavior.
A practical migration sequence for production applications:
1. Run `npx nuxt upgrade --dedupe` and verify the application builds
2. Run the codemod: `npx codemod@latest nuxt/4/migration-recipe`
3. Move files to `app/` (automated by the file-structure codemod)
4. Update `null` checks to `undefined` in data fetching logic
5. Test `` components with normalized names
6. Update CI type checking to use `vue-tsc -b --noEmit`
7. Run the full test suite and fix any TypeScript errors exposed by `noUncheckedIndexedAccess`
For deeper coverage of Vue and Nuxt concepts, explore the [Vue/Nuxt interview questions](/technologies/vue-nuxt/interview-questions/nuxt-fundamentals) on SharpSkill, or review the [SSR and static generation guide](/blog/vue-nuxt/nuxt-3-ssr-static-generation) for background on rendering strategies that carry over to Nuxt 4.
## Conclusion
- Nuxt 4.4 (current as of April 2026) stabilizes the `app/` directory structure, singleton data fetching, and split TypeScript contexts as production-ready defaults
- The `npx codemod@latest nuxt/4/migration-recipe` command automates directory restructuring, deprecated API replacements, and data fetching updates
- Shallow reactivity via `shallowRef` improves performance for read-heavy pages without requiring code changes in most cases
- Separate TypeScript configs per context (app, server, shared, node) eliminate cross-context type leaks and improve IDE autocompletion
- Nuxt 3 reaches end-of-life on July 31, 2026 — migrating before that date keeps applications on a supported, actively maintained version
- Vue Router v5 and Unhead v2 bring cleaner APIs at the cost of removing deprecated properties that should be audited during migration
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/en/blog/vue-nuxt/nuxt-4-directory-structure-migration