---
title: "Nuxt 4.5: Vite 8, SSR streaming, stable error codes and useLayout() · vuejs.school"
description: "Nuxt 4.5 moves to Vite 8, adds experimental SSR streaming, coded errors, useLayout, named views and an enabled option for useFetch. Here is the upgrade."
canonical_url: "https://vuejs.school/community/blog/nuxt-4-5-vite-8-ssr-streaming-and-uselayout"
meta:
  "og:description": "Nuxt 4.5 moves to Vite 8, adds experimental SSR streaming, coded errors, useLayout, named views and an enabled option for useFetch. Here is the upgrade."
  "og:image": "https://vuejs.school/_og/d/a_~TnV4dCA0LjU6IFZpdGUgOCwgU1NSIHN0cmVhbWluZywgc3RhYmxlIGVycm9yIGNvZGVzIGFuZCB1c2VMYXlvdXQoKSDCtyB2dWVqcy5zY2hvb2w%2Cc_OgCard.takumi%2Ctitle_~TnV4dCA0LjU6IFZpdGUgOCwgU1NSIHN0cmVhbWluZywgc3RhYmxlIGVycm9yIGNvZGVzIGFuZCB1c2VMYXlvdXQoKQ%2Cdescription_~TnV4dCA0LjUgbW92ZXMgdG8gVml0ZSA4LCBhZGRzIGV4cGVyaW1lbnRhbCBTU1Igc3RyZWFtaW5nLCBjb2RlZCBlcnJvcnMsIHVzZUxheW91dCwgbmFtZWQgdmlld3MgYW5kIGFuIGVuYWJsZWQgb3B0aW9uIGZvciB1c2VGZXRjaC4gSGVyZSBpcyB0aGUgdXBncmFkZS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sky%2Cp_Ii9jb21tdW5pdHkvYmxvZy9udXh0LTQtNS12aXRlLTgtc3NyLXN0cmVhbWluZy1hbmQtdXNlbGF5b3V0Ig%2Cs_RkiNI0W012kRclyh.png"
  "og:image:alt": "Nuxt 4.5: Vite 8, SSR streaming, stable error codes and useLayout() · vuejs.school"
  "og:image:height": "600"
  "og:image:type": image/png
  "og:image:width": "1200"
  "og:title": "Nuxt 4.5: Vite 8, SSR streaming, stable error codes and useLayout() · vuejs.school"
  "twitter:image": "https://vuejs.school/_og/d/a_~TnV4dCA0LjU6IFZpdGUgOCwgU1NSIHN0cmVhbWluZywgc3RhYmxlIGVycm9yIGNvZGVzIGFuZCB1c2VMYXlvdXQoKSDCtyB2dWVqcy5zY2hvb2w%2Cc_OgCard.takumi%2Ctitle_~TnV4dCA0LjU6IFZpdGUgOCwgU1NSIHN0cmVhbWluZywgc3RhYmxlIGVycm9yIGNvZGVzIGFuZCB1c2VMYXlvdXQoKQ%2Cdescription_~TnV4dCA0LjUgbW92ZXMgdG8gVml0ZSA4LCBhZGRzIGV4cGVyaW1lbnRhbCBTU1Igc3RyZWFtaW5nLCBjb2RlZCBlcnJvcnMsIHVzZUxheW91dCwgbmFtZWQgdmlld3MgYW5kIGFuIGVuYWJsZWQgb3B0aW9uIGZvciB1c2VGZXRjaC4gSGVyZSBpcyB0aGUgdXBncmFkZS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sky%2Cp_Ii9jb21tdW5pdHkvYmxvZy9udXh0LTQtNS12aXRlLTgtc3NyLXN0cmVhbWluZy1hbmQtdXNlbGF5b3V0Ig%2Cs_RkiNI0W012kRclyh.png"
  "twitter:image:alt": "Nuxt 4.5: Vite 8, SSR streaming, stable error codes and useLayout() · vuejs.school"
---

> Index of every page on this site: [llms.txt](https://vuejs.school/llms.txt)

## What shipped

The release post lists these headline changes:

- **Vite 8** in the default Vite builder, and **Rspack 2** for apps that use `builder: 'rspack'`, with that builder rebuilt on Rsbuild.
- **Experimental SSR streaming**, which sends the start of the page before the rest has rendered.
- **Stable error codes** such as `NUXT_E1001`, each with an explanation, a fix and, for the harder ones, a docs page.
- **`useLayout()`**, a read-only computed ref with the layout resolved for the current route.
- **Named views** through a `name@view.vue` file name convention.
- **An `enabled` option** for `useFetch` and `useAsyncData`.

Smaller changes in the same release:

- `<NuxtLink custom>` now exposes `prefetch`, `prefetched` and `shouldPrefetch` in its slot.
- `import.meta.envName` gives you the environment name at runtime.
- An opt-in `experimental.prefetchPreloadTags` warms the next page's preload hints when you prefetch a link to it.
- Head management moves to unhead v3, which types `useHead` more strictly (see the upgrade checks below).

The release also announces that Nuxt 3 reaches end of life on 31 July, with 3.21.9 published alongside as one of the last 3.x patches.

## Vite 8 and your custom Vite config

Nuxt 4.4 built with Vite 7. Nuxt 4.5's Vite builder depends on Vite `^8.1.4`, and Vite 8 changes the tools underneath:

- Rolldown, a bundler written in Rust, replaces both esbuild (for pre-bundling dependencies in development) and Rollup (for production builds).
- Oxc, a set of Rust tools for JavaScript, takes over the code transforms esbuild used to do.

The release post calls this a transparent upgrade for most apps. The apps it asks to check are the ones that depend on Vite directly: options under the `vite` key in `nuxt.config.ts`, Vite plugins you add yourself, or ecosystem plugins that pin a Vite version.

If you do pass options to Vite, read them against the [Vite migration guide](https://vite.dev/guide/migration). Several are renamed and still work through a compatibility layer, with a deprecation:

- `vite.build.rollupOptions` becomes `vite.build.rolldownOptions`.
- `vite.esbuild` becomes `vite.oxc`.
- `vite.optimizeDeps.esbuildOptions` becomes `vite.optimizeDeps.rolldownOptions`.

One setting is not translated at all. The object form of `manualChunks`, often used to keep a heavy library in its own file, is no longer supported (the function form still works, but is deprecated):

*nuxt.config.ts**Breaks***

```
export default defineNuxtConfig({
  vite: {
    build: {
      rollupOptions: {
        output: {
          manualChunks: { charts: ['chart.js'] },
        },
      },
    },
  },
})
```

Rolldown's replacement is `codeSplitting`, which takes groups, each with a name and a test against the module path:

*nuxt.config.ts**Works***

```
export default defineNuxtConfig({
  vite: {
    build: {
      rolldownOptions: {
        output: {
          codeSplitting: {
            groups: [{ name: 'charts', test: /node_modules[\\/]chart\.js[\\/]/ }],
          },
        },
      },
    },
  },
})
```

The `[\\/]` parts of the test match both `/` and `\`, so the group also works with Windows paths.

Vite plugins you add yourself need a version that supports Vite 8, so check each one's changelog before you upgrade production.

## Experimental SSR streaming

Without streaming, the server renders the whole page and then sends it. With `experimental.ssrStreaming`, Nuxt sends the HTML shell first (the `<head>`, styles, preload hints and entry scripts) and streams the body as Vue renders it. The aim is a lower Time to First Byte, the time until the browser receives anything at all.

*nuxt.config.ts*

```
export default defineNuxtConfig({
  experimental: {
    ssrStreaming: true,
  },
  routeRules: {
    '/account/**': { streaming: false },
  },
})
```

The `experimental` flag turns streaming on for every route, and the `routeRules` entry turns it off again for everything under `/account/`. Search engine crawlers always get the fully rendered page; a `botRegex` option controls what counts as a crawler.

Read the catch before you turn it on. The HTTP status and headers leave the server with the shell, so nothing can change them afterwards. According to the [experimental features docs](https://nuxt.com/docs/4.x/guide/going-further/experimental-features#ssrstreaming), changes made with these calls never reach the browser if they run while components render, including after an `await` in `<script setup>` or route middleware:

- `setResponseStatus()`
- `useResponseHeader()`
- a `useCookie()` write
- h3's `setHeader()` and `appendResponseHeader()`

Nuxt plugins run to completion before rendering starts, so changes made there do reach the browser. Nuxt also falls back to normal rendering on its own for routes with `redirect`, `cache`, `isr`, `swr` or `noScripts` rules, or `ssr: false`. In development it logs a warning naming any dropped change, so nothing fails silently.

The practical case to check is a page that sets a 404 after loading its data. Under streaming its status is already 200 by then. Move that logic into a plugin, or opt the route out with `streaming: false`.

## Stable error codes you can search for

Nuxt now raises its warnings and errors through nostics, the same structured-diagnostics library Pinia adopted last week. Each one has a code such as `NUXT_E1001` or `NUXT_B5001`, a short reason and a concrete fix, and the codes are meant to stay the same between releases, so you can search for them.

The first example in the release post is the classic one: a composable such as `useRoute()` or `useFetch()` called outside a Nuxt context, usually inside a `setTimeout` or after an `await` in a plain function. That is now `NUXT_E1001`, and its [docs page](https://nuxt.com/docs/4.x/errors/e1001) explains the rule: call the composable at the top of `setup()`, a plugin or middleware, and use `nuxtApp.runWithContext()` for async server work.

Production builds keep the code and drop the long text. The team plans to move more existing messages onto the system in coming releases.

## Read the current layout with useLayout

`useLayout()` answers "which layout is this page using?" from anywhere. It resolves the layout the same way `<NuxtLayout>` does: the page's `layout` meta first, then a layout set through route rules, then `default`. It returns a read-only computed ref holding the name, or `false` when the layout is turned off, and it updates as the route changes:

*app/app.vue*

```
<script setup lang="ts">
const layout = useLayout()
</script>

<template>
  <SupportChat v-if="layout !== 'checkout'" />
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>
```

`SupportChat` stands for a component of your own, hidden here on pages that use a `checkout` layout. `app.vue` sits outside `<NuxtLayout>`, and `useLayout()` still works there: it returns the layout the current route resolves to.

## Fill two outlets with named views

An outlet is a `<NuxtPage>` where a child page renders, and named views let one route fill two of them. Vue Router has supported them for years; Nuxt 4.5 maps them to files. A file named `name@view.vue` next to a page is rendered into the `<NuxtPage>` with that `name`:

*app/pages*

```
docs.vue
docs/
  getting-started.vue
  getting-started@sidebar.vue
```

The parent page, `docs.vue`, renders both outlets and gives the second one its name:

*app/pages/docs.vue*

```
<template>
  <div>
    <NuxtPage />
    <aside>
      <NuxtPage name="sidebar" />
    </aside>
  </div>
</template>
```

At `/docs/getting-started`, `getting-started.vue` fills the main outlet and `getting-started@sidebar.vue` fills the aside. An outlet with no matching file stays empty. `definePageMeta` is only read from the main file, so put middleware and layout settings there.

## Hold back useFetch with the enabled option

Until now, stopping `useFetch` from running before some condition was true meant `immediate: false` plus a watcher that called `execute()`. The new `enabled` option takes a boolean, a ref or a getter, and works the same way in `useAsyncData`. While it is `false`, nothing runs: not the first fetch, not `refresh()`, not a watch trigger. Switching it from `true` to `false` cancels a request in flight and keeps the existing `data`.

*app/pages/search.vue*

```
<script setup lang="ts">
const query = ref('')

const { data, status } = await useFetch('/api/search', {
  query: { q: query },
  enabled: () => query.value.length > 2,
})
</script>

<template>
  <input v-model="query" type="search" aria-label="Search">
  <p v-if="status === 'pending'">Searching…</p>
  <ul v-else-if="data">
    <li v-for="hit in data" :key="hit.id">{{ hit.title }}</li>
  </ul>
</template>
```

Typing a third character changes `query`, which the fetch watches, and `enabled` is now true, so the request goes out. One detail from the `useFetch` docs: turning `enabled` back on does not fetch by itself. A change to a watched value, or a call to `refresh()`, does.

## How to upgrade

The release post recommends one command, which also deduplicates your lockfile so the many dependency upgrades in this release land together:

```
npx nuxt upgrade --dedupe
```

Then check three things before you deploy:

1. **Node.** Nuxt 4.5's `engines` field asks for Node 22.19 or later on the 22 line (4.4 accepted 22.12), 24.11 or later, or 26.
2. **Head types.** unhead v3 types `useHead` more strictly, and promise input, deprecated in v2, is gone. The release post says new type errors are almost always genuine tightening, so fix them rather than cast them away.
3. **Custom Vite config and plugins**, as described above.

If you are still on Nuxt 3, there is a 3.x path too, `npx nuxt@latest upgrade --dedupe --channel=v3`, but with end of life ten days away, use this week to plan the move to Nuxt 4 instead.

**Check it stuck**3 questions from the Server routes quiz.

[**Practice now**](https://vuejs.school/learn/nuxt/server-routes?tab=quiz)

**Was this helpful?**

## **Keep reading**

[Release notes · 7 min read**What's new in Nuxt 4.6: nuxt/server, typed $fetch, cookie sessions and Vapor**](https://vuejs.school/community/blog/whats-new-in-nuxt-4-6) [Tutorial · 6 min read**useFetch or $fetch? Picking the right one in Nuxt**](https://vuejs.school/community/blog/usefetch-or-fetch-picking-the-right-one-in-nuxt)

```json
{"@context":"https://schema.org","@graph":[{"@id":"https://vuejs.school/#website","@type":"WebSite","description":"A free AI-instructed school for Vue.js. Short tutorials, runnable code and quizzes that adapt to you.","name":"vuejs.school","url":"https://vuejs.school/","publisher":{"@id":"https://vuejs.school/#identity"}},{"@id":"https://vuejs.school/community/blog/nuxt-4-5-vite-8-ssr-streaming-and-uselayout#webpage","@type":"WebPage","dateModified":"2026-10-27T19:31:40.253Z","datePublished":"2026-07-21","description":"Nuxt 4.5 moves to Vite 8, adds experimental SSR streaming, coded errors, useLayout, named views and an enabled option for useFetch. Here is the upgrade.","name":"Nuxt 4.5: Vite 8, SSR streaming, stable error codes and useLayout()","url":"https://vuejs.school/community/blog/nuxt-4-5-vite-8-ssr-streaming-and-uselayout","breadcrumb":{"@id":"https://vuejs.school/community/blog/nuxt-4-5-vite-8-ssr-streaming-and-uselayout#breadcrumb"},"isPartOf":{"@id":"https://vuejs.school/#website"},"potentialAction":[{"@type":"ReadAction","target":["https://vuejs.school/community/blog/nuxt-4-5-vite-8-ssr-streaming-and-uselayout"]}],"primaryImageOfPage":{"@id":"https://vuejs.school/#logo"}},{"@id":"https://vuejs.school/#identity","@type":"Organization","description":"A free AI-instructed school for Vue.js. Short tutorials, runnable code and quizzes that adapt to you.","name":"vuejs.school","url":"https://vuejs.school","logo":{"@id":"https://vuejs.school/#logo"}},{"@id":"https://vuejs.school/community/blog/nuxt-4-5-vite-8-ssr-streaming-and-uselayout#article","dateModified":"2026-10-27T19:31:40.253Z","datePublished":"2026-07-21","description":"Nuxt 4.5 moves to Vite 8, adds experimental SSR streaming, coded errors, useLayout, named views and an enabled option for useFetch. Here is the upgrade.","headline":"Nuxt 4.5: Vite 8, SSR streaming, stable error codes and useLayout()","inLanguage":"en","isAccessibleForFree":true,"thumbnailUrl":"https://vuejs.school/_og/d/a_~TnV4dCA0LjU6IFZpdGUgOCwgU1NSIHN0cmVhbWluZywgc3RhYmxlIGVycm9yIGNvZGVzIGFuZCB1c2VMYXlvdXQoKSDCtyB2dWVqcy5zY2hvb2w%2Cc_OgCard.takumi%2Ctitle_~TnV4dCA0LjU6IFZpdGUgOCwgU1NSIHN0cmVhbWluZywgc3RhYmxlIGVycm9yIGNvZGVzIGFuZCB1c2VMYXlvdXQoKQ%2Cdescription_~TnV4dCA0LjUgbW92ZXMgdG8gVml0ZSA4LCBhZGRzIGV4cGVyaW1lbnRhbCBTU1Igc3RyZWFtaW5nLCBjb2RlZCBlcnJvcnMsIHVzZUxheW91dCwgbmFtZWQgdmlld3MgYW5kIGFuIGVuYWJsZWQgb3B0aW9uIGZvciB1c2VGZXRjaC4gSGVyZSBpcyB0aGUgdXBncmFkZS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sky%2Cp_Ii9jb21tdW5pdHkvYmxvZy9udXh0LTQtNS12aXRlLTgtc3NyLXN0cmVhbWluZy1hbmQtdXNlbGF5b3V0Ig%2Cs_RkiNI0W012kRclyh.png","@type":["Article","BlogPosting"],"articleSection":["Release notes"],"author":{"@id":"https://vuejs.school/#identity"},"image":{"@id":"https://vuejs.school/#/schema/image/1"},"isPartOf":{"@id":"https://vuejs.school/community/blog/nuxt-4-5-vite-8-ssr-streaming-and-uselayout#webpage"},"mainEntityOfPage":{"@id":"https://vuejs.school/community/blog/nuxt-4-5-vite-8-ssr-streaming-and-uselayout#webpage"},"publisher":{"@id":"https://vuejs.school/#identity"}},{"@id":"https://vuejs.school/community/blog/nuxt-4-5-vite-8-ssr-streaming-and-uselayout#breadcrumb","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","name":"Blog","item":"https://vuejs.school/community/blog","position":1},{"@type":"ListItem","name":"Nuxt 4.5: Vite 8, SSR streaming, stable error codes and useLayout()","position":2}]},{"@id":"https://vuejs.school/#logo","@type":"ImageObject","caption":"vuejs.school","contentUrl":"https://vuejs.school/apple-touch-icon.png","inLanguage":"en","url":"https://vuejs.school/apple-touch-icon.png"},{"@id":"https://vuejs.school/#/schema/image/1","@type":"ImageObject","contentUrl":"https://vuejs.school/_og/d/a_~TnV4dCA0LjU6IFZpdGUgOCwgU1NSIHN0cmVhbWluZywgc3RhYmxlIGVycm9yIGNvZGVzIGFuZCB1c2VMYXlvdXQoKSDCtyB2dWVqcy5zY2hvb2w%2Cc_OgCard.takumi%2Ctitle_~TnV4dCA0LjU6IFZpdGUgOCwgU1NSIHN0cmVhbWluZywgc3RhYmxlIGVycm9yIGNvZGVzIGFuZCB1c2VMYXlvdXQoKQ%2Cdescription_~TnV4dCA0LjUgbW92ZXMgdG8gVml0ZSA4LCBhZGRzIGV4cGVyaW1lbnRhbCBTU1Igc3RyZWFtaW5nLCBjb2RlZCBlcnJvcnMsIHVzZUxheW91dCwgbmFtZWQgdmlld3MgYW5kIGFuIGVuYWJsZWQgb3B0aW9uIGZvciB1c2VGZXRjaC4gSGVyZSBpcyB0aGUgdXBncmFkZS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sky%2Cp_Ii9jb21tdW5pdHkvYmxvZy9udXh0LTQtNS12aXRlLTgtc3NyLXN0cmVhbWluZy1hbmQtdXNlbGF5b3V0Ig%2Cs_RkiNI0W012kRclyh.png","inLanguage":"en","url":"https://vuejs.school/_og/d/a_~TnV4dCA0LjU6IFZpdGUgOCwgU1NSIHN0cmVhbWluZywgc3RhYmxlIGVycm9yIGNvZGVzIGFuZCB1c2VMYXlvdXQoKSDCtyB2dWVqcy5zY2hvb2w%2Cc_OgCard.takumi%2Ctitle_~TnV4dCA0LjU6IFZpdGUgOCwgU1NSIHN0cmVhbWluZywgc3RhYmxlIGVycm9yIGNvZGVzIGFuZCB1c2VMYXlvdXQoKQ%2Cdescription_~TnV4dCA0LjUgbW92ZXMgdG8gVml0ZSA4LCBhZGRzIGV4cGVyaW1lbnRhbCBTU1Igc3RyZWFtaW5nLCBjb2RlZCBlcnJvcnMsIHVzZUxheW91dCwgbmFtZWQgdmlld3MgYW5kIGFuIGVuYWJsZWQgb3B0aW9uIGZvciB1c2VGZXRjaC4gSGVyZSBpcyB0aGUgdXBncmFkZS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA3IG1pbiByZWFk%2Ctint_sky%2Cp_Ii9jb21tdW5pdHkvYmxvZy9udXh0LTQtNS12aXRlLTgtc3NyLXN0cmVhbWluZy1hbmQtdXNlbGF5b3V0Ig%2Cs_RkiNI0W012kRclyh.png"}]}
```