---
title: "Nuxt 4.3: appLayout in routeRules, the #server alias and ISR payloads · vuejs.school"
description: "Nuxt 4.3 sets layouts from routeRules, writes payload files for ISR and SWR routes, adds a #server alias and extends Nuxt 3 support to July."
canonical_url: "https://vuejs.school/community/blog/nuxt-4-3-applayout-server-alias-and-isr-payloads"
meta:
  "og:description": "Nuxt 4.3 sets layouts from routeRules, writes payload files for ISR and SWR routes, adds a #server alias and extends Nuxt 3 support to July."
  "og:image": "https://vuejs.school/_og/d/a_~TnV4dCA0LjM6IGFwcExheW91dCBpbiByb3V0ZVJ1bGVzLCB0aGUgI3NlcnZlciBhbGlhcyBhbmQgSVNSIHBheWxvYWRzIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~TnV4dCA0LjM6IGFwcExheW91dCBpbiByb3V0ZVJ1bGVzLCB0aGUgI3NlcnZlciBhbGlhcyBhbmQgSVNSIHBheWxvYWRz%2Cdescription_~TnV4dCA0LjMgc2V0cyBsYXlvdXRzIGZyb20gcm91dGVSdWxlcywgd3JpdGVzIHBheWxvYWQgZmlsZXMgZm9yIElTUiBhbmQgU1dSIHJvdXRlcywgYWRkcyBhICNzZXJ2ZXIgYWxpYXMgYW5kIGV4dGVuZHMgTnV4dCAzIHN1cHBvcnQgdG8gSnVseS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA4IG1pbiByZWFk%2Ctint_sky%2Cp_Ii9jb21tdW5pdHkvYmxvZy9udXh0LTQtMy1hcHBsYXlvdXQtc2VydmVyLWFsaWFzLWFuZC1pc3ItcGF5bG9hZHMi%2Cs_tLXEjyw8xTdtyBnU.png"
  "og:image:alt": "Nuxt 4.3: appLayout in routeRules, the #server alias and ISR payloads · vuejs.school"
  "og:image:height": "600"
  "og:image:type": image/png
  "og:image:width": "1200"
  "og:title": "Nuxt 4.3: appLayout in routeRules, the #server alias and ISR payloads · vuejs.school"
  "twitter:image": "https://vuejs.school/_og/d/a_~TnV4dCA0LjM6IGFwcExheW91dCBpbiByb3V0ZVJ1bGVzLCB0aGUgI3NlcnZlciBhbGlhcyBhbmQgSVNSIHBheWxvYWRzIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~TnV4dCA0LjM6IGFwcExheW91dCBpbiByb3V0ZVJ1bGVzLCB0aGUgI3NlcnZlciBhbGlhcyBhbmQgSVNSIHBheWxvYWRz%2Cdescription_~TnV4dCA0LjMgc2V0cyBsYXlvdXRzIGZyb20gcm91dGVSdWxlcywgd3JpdGVzIHBheWxvYWQgZmlsZXMgZm9yIElTUiBhbmQgU1dSIHJvdXRlcywgYWRkcyBhICNzZXJ2ZXIgYWxpYXMgYW5kIGV4dGVuZHMgTnV4dCAzIHN1cHBvcnQgdG8gSnVseS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA4IG1pbiByZWFk%2Ctint_sky%2Cp_Ii9jb21tdW5pdHkvYmxvZy9udXh0LTQtMy1hcHBsYXlvdXQtc2VydmVyLWFsaWFzLWFuZC1pc3ItcGF5bG9hZHMi%2Cs_tLXEjyw8xTdtyBnU.png"
  "twitter:image:alt": "Nuxt 4.3: appLayout in routeRules, the #server alias and ISR payloads · vuejs.school"
---

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

## What shipped

The [release post](https://nuxt.com/blog/v4-3) lists these changes:

- A new `appLayout` property in `routeRules`, so a layout can be set for a whole path pattern instead of in each page's `definePageMeta`.
- Payload extraction for routes with `isr`, `swr` and `cache` rules, which used to work only for prerendered pages, and payload extraction in development.
- Route groups, the folders wrapped in parentheses such as `(protected)/`, now appear in `route.meta.groups`.
- `setPageLayout()` accepts a second argument with props for the layout.
- A `#server` alias for imports inside the `server/` directory, with protection against importing it from client or shared code.
- The development error overlay from Nuxt 4.2 can be dragged to a corner, minimised to a small pill, and keeps its position across reloads.
- A module inherited from a layer can be disabled by passing `false` as its options, for example `image: false`.
- `statusCode` and `statusMessage` on `createError` are deprecated in favour of `status` and `statusText`.
- Module authors can pass async functions to `addVitePlugin` and `addWebpackPlugin`, and `inlineStyles` now works with webpack and rspack as well as Vite.

Under the hood, route rules are now compiled into a client chunk with `rou3`, which removes `radix3` from the client bundle and the app manifest fetches that went with it. The plugin that handles SSR styles got faster, and internal hooks use filters so they run less often.

## Set a layout for a whole path with `appLayout`

Until now a page chose its layout itself, with `definePageMeta({ layout: 'admin' })` in its script block. For an admin area with thirty pages that is thirty identical lines, and a new page that forgets the line renders in the default layout. From 4.3 the layout can follow the URL instead:

*nuxt.config.ts*

```
export default defineNuxtConfig({
  routeRules: {
    '/admin/**': { appLayout: 'admin' },
    '/auth/**': { appLayout: 'minimal' },
  },
})
```

`routeRules` is where Nuxt already keeps per-path settings such as caching, redirects and headers. The keys are path patterns, and `**` matches any depth. `appLayout` names a file in `app/layouts/`, so `'admin'` means `app/layouts/admin.vue`. Every page under `/admin/` now renders inside it, with no `definePageMeta` call.

`appLayout` only sets a default. A layout set in the page with `definePageMeta({ layout })`, or by a `setPageLayout()` call, takes priority, so a single page can still pick another one.

Layouts can also receive props. `setPageLayout()`, the composable that switches the layout at runtime, takes a second argument, and the layout declares the props as any component would:

*app/middleware/admin.ts*

```
export default defineNuxtRouteMiddleware(() => {
  setPageLayout('admin', { sidebar: true, theme: 'dark' })
})
```

*app/layouts/admin.vue*

```
<script setup lang="ts">
defineProps<{
  sidebar?: boolean
  theme?: 'light' | 'dark'
}>()
</script>

<template>
  <div :data-theme="theme">
    <aside v-if="sidebar">Admin menu</aside>
    <slot />
  </div>
</template>
```

## Payload files for ISR, SWR and cached routes

When Nuxt renders a page on the server, the data that `useFetch` and `useAsyncData` loaded is serialised into the payload, a JSON blob sent with the HTML so the browser does not fetch it again. For prerendered pages Nuxt has long written that payload to a separate `_payload.json` file next to the HTML. A client-side navigation to such a page loads the small JSON file instead of calling your APIs. Until 4.3, that stopped at pages rendered at build time.

In 4.3 it also applies to pages rendered on request and then cached, the routes with `isr`, `swr` and `cache` rules:

- ISR, incremental static regeneration, renders a page on its first request and serves the cached copy for a set time.
- SWR, stale-while-revalidate, serves the cached copy at once and refreshes it in the background.
- A `cache` rule stores the rendered page on the server with Nitro's cache options, such as `{ maxAge: 60 }` in seconds.

All three now produce a `_payload.json` that a CDN such as Vercel, Netlify or Cloudflare can cache beside the HTML. A navigation from a listing to a product page then fetches one cached file rather than calling every API the page uses.

You do not switch this on: a route that already has one of these rules gets payload files after the upgrade. This rule, for example, caches the product pages with ISR and gives them the `shop` layout:

*nuxt.config.ts*

```
export default defineNuxtConfig({
  routeRules: {
    '/products/**': { isr: 3600, appLayout: 'shop' },
  },
})
```

The `3600` is seconds: a product page is regenerated at most once an hour.

Payload extraction now works in development too, when `nitro.static` is `true` or when a page has an `isr`, `swr`, `prerender` or `cache` rule. You can watch the `_payload.json` request appear in the network tab of your browser's developer tools without making a production build first.

## Check route groups in middleware

Route groups are folders wrapped in parentheses, `app/pages/(protected)/dashboard.vue` for example. The parentheses keep the folder out of the URL, so that page is served at `/dashboard`, and the folder exists only to organise files. From 4.3 the group names are exposed in the route's meta as `groups`, which turns a folder into something middleware can check:

*app/middleware/auth.global.ts*

```
export default defineNuxtRouteMiddleware((to) => {
  const session = useCookie('session')
  if (to.meta.groups?.includes('protected') && !session.value) {
    return navigateTo('/login')
  }
})
```

A middleware file ending in `.global.ts` runs on every navigation. Moving a page into the `(protected)` folder is now enough to put it behind the check, and no page needs a `definePageMeta({ middleware: 'auth' })` line. The `?.` is there because pages outside any group have no `groups` entry. The `session` cookie stands in for however your app knows that someone is signed in.

## Import server code with `#server`

`#shared` already pointed at the `shared/` folder. Now `#server` points at `server/`, so a handler deep in the tree imports a helper without counting dots:

*server/api/products/\[id].get.ts*

```
import { findProduct } from '#server/utils/products'

export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id')
  const product = await findProduct(id)
  if (!product) {
    throw createError({ status: 404, statusText: 'Product not found' })
  }
  return product
})
```

The alias comes with import protection. If a component, a composable or a file in `shared/` imports from `#server`, Nuxt refuses the import, so server code such as a database client cannot reach the browser bundle through the alias.

## Rename `statusCode` and `statusMessage` in `createError`

The handler above already uses the new error fields. Nitro v3 and h3 v2 are moving to the names the Web API uses: a `Response` has `status` and `statusText`, not `statusCode` and `statusMessage`. Nuxt 4.3 adopts them and deprecates the old names ahead of Nuxt 5. The old names still work and print no warning, so write new code with the new ones and rename old calls when you next touch them:

*server/api/products/\[id].get.ts*

```
// Deprecated in 4.3
throw createError({ statusCode: 404, statusMessage: 'Product not found' })

// Current
throw createError({ status: 404, statusText: 'Product not found' })
```

In pages, components and middleware, `createError` is Nuxt's own, and its types mark the old names `@deprecated`. Your editor's autocomplete lists them as deprecated, but a call that already uses them is not flagged. In `server/`, `createError` comes from h3 1.15, which accepts both spellings and marks neither. The Nuxt docs now use the new names in both places. A search for `statusCode:` across `server/` and `app/` finds every old call.

## Nuxt 3 support runs to 31 July 2026

When Nuxt 4 shipped, the end of life for Nuxt 3 was set for 31 January 2026. The 4.3 post moves it: security updates and critical bug fixes continue, and Nuxt 3 now reaches end of life on 31 July 2026. Nuxt 3.21, released the same day, backports the route rule layouts, the ISR payload extraction, the layout props, the `#server` alias, the draggable overlay, module disabling and the performance work. If you are still on 3.x, take 3.21 now and plan the move to 4 before July, following the [upgrade guide](https://nuxt.com/docs/4.x/getting-started/upgrade).

The post also says the `main` branch will begin receiving commits for Nuxt 5 in the coming weeks, with changes backported to the `4.x` and `3.x` branches, and that the upgrade guide will describe how to opt in early with `future.compatibilityVersion: 5`. That is a plan, not a release.

## How to upgrade and what to check

Upgrade now: the two changes that arrive on their own are quick to check, and the rest waits until you use it. One command updates Nuxt and deduplicates the lockfile, which pulls in updates to the packages Nuxt relies on:

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

For a project staying on Nuxt 3, the same command takes a channel:

```
npx nuxt@latest upgrade --dedupe --channel=v3
```

Then run `npx nuxt typecheck`. The server `tsconfig` now enables `noUncheckedIndexedAccess`, which types every array element and every dynamic object key as possibly `undefined`. A line such as `const first = rows[0]` followed by `first.id` now reports that `first` is possibly undefined. The fix is a guard or an early `createError`.

The release post recommends keeping the option on, because turning it off lets indexed reads that can fail at runtime slip through. It shows the opt-out for a codebase that needs time:

*nuxt.config.ts*

```
export default defineNuxtConfig({
  nitro: {
    typescript: {
      tsConfig: {
        compilerOptions: { noUncheckedIndexedAccess: false },
      },
    },
  },
})
```

The TypeScript config also enables `allowArbitraryExtensions` by default. It lets you import a file with an unusual extension when a declaration file named like `styles.d.css.ts` sits beside it, so it only adds possibilities.

Last, if you have `isr`, `swr` or `cache` rules, click through to one of those pages and check that its `_payload.json` request appears in the network tab.

**Check it stuck**3 questions from the Rendering modes and deployment quiz.

[**Practice now**](https://vuejs.school/learn/nuxt/rendering-modes-and-deployment?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-3-applayout-server-alias-and-isr-payloads#webpage","@type":"WebPage","dateModified":"2026-10-27T19:31:40.253Z","datePublished":"2026-01-27","description":"Nuxt 4.3 sets layouts from routeRules, writes payload files for ISR and SWR routes, adds a #server alias and extends Nuxt 3 support to July.","name":"Nuxt 4.3: appLayout in routeRules, the #server alias and ISR payloads","url":"https://vuejs.school/community/blog/nuxt-4-3-applayout-server-alias-and-isr-payloads","breadcrumb":{"@id":"https://vuejs.school/community/blog/nuxt-4-3-applayout-server-alias-and-isr-payloads#breadcrumb"},"isPartOf":{"@id":"https://vuejs.school/#website"},"potentialAction":[{"@type":"ReadAction","target":["https://vuejs.school/community/blog/nuxt-4-3-applayout-server-alias-and-isr-payloads"]}],"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-3-applayout-server-alias-and-isr-payloads#article","dateModified":"2026-10-27T19:31:40.253Z","datePublished":"2026-01-27","description":"Nuxt 4.3 sets layouts from routeRules, writes payload files for ISR and SWR routes, adds a #server alias and extends Nuxt 3 support to July.","headline":"Nuxt 4.3: appLayout in routeRules, the #server alias and ISR payloads","inLanguage":"en","isAccessibleForFree":true,"thumbnailUrl":"https://vuejs.school/_og/d/a_~TnV4dCA0LjM6IGFwcExheW91dCBpbiByb3V0ZVJ1bGVzLCB0aGUgI3NlcnZlciBhbGlhcyBhbmQgSVNSIHBheWxvYWRzIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~TnV4dCA0LjM6IGFwcExheW91dCBpbiByb3V0ZVJ1bGVzLCB0aGUgI3NlcnZlciBhbGlhcyBhbmQgSVNSIHBheWxvYWRz%2Cdescription_~TnV4dCA0LjMgc2V0cyBsYXlvdXRzIGZyb20gcm91dGVSdWxlcywgd3JpdGVzIHBheWxvYWQgZmlsZXMgZm9yIElTUiBhbmQgU1dSIHJvdXRlcywgYWRkcyBhICNzZXJ2ZXIgYWxpYXMgYW5kIGV4dGVuZHMgTnV4dCAzIHN1cHBvcnQgdG8gSnVseS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA4IG1pbiByZWFk%2Ctint_sky%2Cp_Ii9jb21tdW5pdHkvYmxvZy9udXh0LTQtMy1hcHBsYXlvdXQtc2VydmVyLWFsaWFzLWFuZC1pc3ItcGF5bG9hZHMi%2Cs_tLXEjyw8xTdtyBnU.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-3-applayout-server-alias-and-isr-payloads#webpage"},"mainEntityOfPage":{"@id":"https://vuejs.school/community/blog/nuxt-4-3-applayout-server-alias-and-isr-payloads#webpage"},"publisher":{"@id":"https://vuejs.school/#identity"}},{"@id":"https://vuejs.school/community/blog/nuxt-4-3-applayout-server-alias-and-isr-payloads#breadcrumb","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","name":"Blog","item":"https://vuejs.school/community/blog","position":1},{"@type":"ListItem","name":"Nuxt 4.3: appLayout in routeRules, the #server alias and ISR payloads","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_~TnV4dCA0LjM6IGFwcExheW91dCBpbiByb3V0ZVJ1bGVzLCB0aGUgI3NlcnZlciBhbGlhcyBhbmQgSVNSIHBheWxvYWRzIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~TnV4dCA0LjM6IGFwcExheW91dCBpbiByb3V0ZVJ1bGVzLCB0aGUgI3NlcnZlciBhbGlhcyBhbmQgSVNSIHBheWxvYWRz%2Cdescription_~TnV4dCA0LjMgc2V0cyBsYXlvdXRzIGZyb20gcm91dGVSdWxlcywgd3JpdGVzIHBheWxvYWQgZmlsZXMgZm9yIElTUiBhbmQgU1dSIHJvdXRlcywgYWRkcyBhICNzZXJ2ZXIgYWxpYXMgYW5kIGV4dGVuZHMgTnV4dCAzIHN1cHBvcnQgdG8gSnVseS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA4IG1pbiByZWFk%2Ctint_sky%2Cp_Ii9jb21tdW5pdHkvYmxvZy9udXh0LTQtMy1hcHBsYXlvdXQtc2VydmVyLWFsaWFzLWFuZC1pc3ItcGF5bG9hZHMi%2Cs_tLXEjyw8xTdtyBnU.png","inLanguage":"en","url":"https://vuejs.school/_og/d/a_~TnV4dCA0LjM6IGFwcExheW91dCBpbiByb3V0ZVJ1bGVzLCB0aGUgI3NlcnZlciBhbGlhcyBhbmQgSVNSIHBheWxvYWRzIMK3IHZ1ZWpzLnNjaG9vbA%2Cc_OgCard.takumi%2Ctitle_~TnV4dCA0LjM6IGFwcExheW91dCBpbiByb3V0ZVJ1bGVzLCB0aGUgI3NlcnZlciBhbGlhcyBhbmQgSVNSIHBheWxvYWRz%2Cdescription_~TnV4dCA0LjMgc2V0cyBsYXlvdXRzIGZyb20gcm91dGVSdWxlcywgd3JpdGVzIHBheWxvYWQgZmlsZXMgZm9yIElTUiBhbmQgU1dSIHJvdXRlcywgYWRkcyBhICNzZXJ2ZXIgYWxpYXMgYW5kIGV4dGVuZHMgTnV4dCAzIHN1cHBvcnQgdG8gSnVseS4%2Ceyebrow_~UmVsZWFzZSBub3RlcyDCtyA4IG1pbiByZWFk%2Ctint_sky%2Cp_Ii9jb21tdW5pdHkvYmxvZy9udXh0LTQtMy1hcHBsYXlvdXQtc2VydmVyLWFsaWFzLWFuZC1pc3ItcGF5bG9hZHMi%2Cs_tLXEjyw8xTdtyBnU.png"}]}
```