Экспериментальные возможности

Включение экспериментальных возможностей Nuxt для доступа к новому функционалу.

В Nuxt часть возможностей помечена как экспериментальные; их можно включить в конфигурации.

Описание этих возможностей задаётся в @nuxt/schema. Полный список и параметры — в API-документации и исходном коде.

Экспериментальные возможности могут быть изменены или удалены в будущих версиях.

alwaysRunFetchOnKeyChange

Запускать ли useFetch при изменении ключа, даже если задано immediate: false и запрос ещё не выполнялся.

useFetch и useAsyncData всегда перезапускаются при смене ключа, если immediate: true или запрос уже был выполнен.

По умолчанию флаг выключен; при необходимости включите:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    alwaysRunFetchOnKeyChange: true,
  },
})

appManifest

Использовать app manifest, чтобы на клиенте учитывались route rules.

По умолчанию включено; при необходимости отключите:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    appManifest: false,
  },
})

asyncContext

Включить нативный async context для вложенных композаблов в Nuxt и Nitro — возможность вызывать композаблы внутри асинхронных и снизить ошибки «Nuxt instance is unavailable».

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    asyncContext: true,
  },
})
Подробнее в pull-request на GitHub.

asyncEntry

Включает генерацию асинхронной точки входа для Vue-бандла, что помогает при использовании module federation.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    asyncEntry: true,
  },
})

extractAsyncDataHandlers

Выносит функции-обработчики из вызовов useAsyncData и useLazyAsyncData в отдельные чанки для улучшения code splitting и кэширования.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    extractAsyncDataHandlers: true,
  },
})

Обработчики, заданные inline, превращаются в динамически подгружаемые чанки:

<!-- До -->
<script setup>
const { data } = await useAsyncData('user', async () => {
  return await $fetch('/api/user')
})
</script>
<!-- После преобразования -->
<script setup>
const { data } = await useAsyncData('user', () =>
  import('/generated-chunk.js').then(r => r.default()),
)
</script>

Так логика загрузки данных выносится в отдельные чанки, но при необходимости код по-прежнему подгружается.

Рекомендуется только для статической сборки с извлечением payload, когда данные не нужно подгружать заново в рантайме.

emitRouteChunkError

Вызывает хук app:chunkError при ошибке загрузки чанков vite/webpack. По умолчанию при переходе на новый маршрут при сбое загрузки чанка выполняется перезагрузка нового маршрута.

По умолчанию Nuxt перезагружает новый маршрут при сбое загрузки чанка при навигации (automatic).

Значение automatic-immediate заставляет Nuxt перезагружать текущий маршрут сразу при сбое загрузки чанка (не дожидаясь навигации). Это полезно для ошибок чанков, не связанных с навигацией, например когда не удаётся загрузить ленивый компонент. Минус — возможные лишние перезагрузки, если приложению не нужен чанк, из-за которого произошла ошибка.

Автоматическую обработку можно отключить, задав false, или обрабатывать ошибки чанков вручную, задав manual.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    emitRouteChunkError: 'automatic', // или 'automatic-immediate', 'manual' или false
  },
})

enforceModuleCompatibility

Нужно ли выбрасывать ошибку (и не загружать приложение), если модуль Nuxt несовместим.

По умолчанию отключено.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    enforceModuleCompatibility: true,
  },
})

restoreState

Позволяет восстанавливать состояние приложения Nuxt из sessionStorage при перезагрузке страницы после ошибки чанка или вызова reloadNuxtApp().

Чтобы избежать ошибок гидрации, состояние применяется только после монтирования Vue-приложения, из-за чего при первой загрузке возможна краткая «вспышка».

Включайте с осторожностью: возможны неожиданные эффекты. Рекомендуется задавать явные ключи в useState, так как автогенерируемые ключи могут не совпадать между сборками.
nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    restoreState: true,
  },
})

inlineRouteRules

Задаёт правила маршрутов на уровне страницы через defineRouteRules.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    inlineRouteRules: true,
  },
})

Правила создаются по path страницы.

Подробнее в утилите defineRouteRules.
Узнать больше Docs > 4 X > Guide > Concepts > Rendering#hybrid Rendering.

noVueServer

Отключает эндпоинт Vue server renderer в Nitro.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    noVueServer: true,
  },
})

parseErrorData

Нужно ли разбирать error.data при рендере страницы серверной ошибки.

По умолчанию включено; при необходимости можно отключить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    parseErrorData: false,
  },
})

payloadExtraction

Управляет тем, как передаются данные payload для пререндеренных и кэшируемых (ISR/SWR) страниц.

  • 'client' — payload встраивается в HTML при первом серверном рендере и выносится в файлы _payload.json для клиентской навигации. Нет отдельного сетевого запроса при первой загрузке и сохраняется эффективная клиентская навигация.
  • true — payload выносится в отдельный _payload.json и для первого рендера, и для клиентской навигации.
  • false — извлечение payload отключено. Payload всегда в HTML, файлы _payload.json не создаются.

По умолчанию true, при compatibilityVersion: 5'client'.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    // Payload в HTML, в файлы — только для клиентской навигации
    payloadExtraction: 'client',
  },
})

Извлечение payload работает и для маршрутов с кэшированием ISR (Incremental Static Regeneration) или SWR (Stale-While-Revalidate). CDN могут кэшировать файлы payload вместе с HTML и ускорять клиентскую навигацию по кэшированным маршрутам.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    payloadExtraction: 'client',
  },
  routeRules: {
    // Для этих маршрутов будут создаваться файлы payload
    '/products/**': { isr: 3600 },
    '/blog/**': { swr: true },
  },
})

clientNodePlaceholder

Uses comment nodes (<!--placeholder-->) instead of <div> elements as placeholders for client-only components during server-side rendering.

When enabled, .client.vue components and createClientOnly() wrappers render an HTML comment on the server instead of an empty <div>. This fixes a Vue hydration issue where scoped styles may not be applied when the placeholder <div> and the actual component root share the same tag name.

Enabling this means attributes (class, style, etc.) passed to .client.vue components will not appear in the SSR HTML. If you need styled placeholders to prevent layout shift, use <ClientOnly> with a #fallback slot instead.

This flag is enabled when future.compatibilityVersion is set to 5 or higher, but you can also enable it explicitly:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    clientNodePlaceholder: true,
  },
})

clientFallback

Включает экспериментальный компонент <NuxtClientFallback> для рендера контента на клиенте при ошибке SSR.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    clientFallback: true,
  },
})

crossOriginPrefetch

Включает cross-origin prefetch через Speculation Rules API.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    crossOriginPrefetch: true,
  },
})
Подробнее о Speculation Rules API.

viewTransition

Включает интеграцию View Transition API с клиентским роутером.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    viewTransition: true,
  },
})

Можно передать объект для настройки типов view transition — разных CSS-анимаций в зависимости от типа навигации:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    viewTransition: {
      enabled: true,
      types: ['slide'],
    },
  },
})
Прочитайте и отредактируйте живой пример в https://stackblitz.com/edit/nuxt-view-transitions?file=app.vue.
Подробнее о View Transition API.
Подробнее о View Transition API.

writeEarlyHints

Включает отправку early hints при использовании node-сервера.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    writeEarlyHints: true,
  },
})

componentIslands

Включает экспериментальную поддержку component islands с <NuxtIsland> и файлами .island.vue.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    componentIslands: true, // false или 'local+remote'
  },
})
Узнать больше Docs > 4 X > Directory Structure > App > Components#server Components.
Дорожная карта server components на GitHub.

localLayerAliases

Разрешать алиасы ~, ~~, @ и @@ внутри слоёв относительно исходной и корневой директорий слоя.

По умолчанию включено; при необходимости можно отключить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    localLayerAliases: false,
  },
})

typedPages

Включает новый экспериментальный типизированный роутер.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    typedPages: true,
  },
})

По умолчанию даёт типизированное использование navigateTo, <NuxtLink>, router.push() и др.

Типизированные параметры в странице: const route = useRoute('route-name').

watcher

Задаёт альтернативный watcher для отслеживания файлов в Nuxt.

По умолчанию используется chokidar-granular, который игнорирует корневые каталоги (например node_modules и .git).

Значение parcel подключает @parcel/watcher и может улучшить производительность в больших проектах и на Windows.

Значение chokidar включает отслеживание всех файлов в исходной директории.

Значение 'builder' переиспользует собственный file watcher активного сборщика (например, server.watcher у Vite) вместо запуска второго. Это уменьшает число watcher'ов в режиме разработки и станет значением по умолчанию при future.compatibilityVersion: 5. Если у сборщика нет своего watcher'а (сейчас webpack и rspack), Nuxt выводит предупреждение и возвращается к выбору по умолчанию.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    watcher: 'chokidar-granular', // также возможны 'chokidar', 'parcel' или 'builder'
  },
})

sharedPrerenderData

Nuxt автоматически делится данными payload между пререндеренными страницами. Это может заметно ускорить пререндер сайтов с useAsyncData или useFetch, когда одни и те же данные запрашиваются на разных страницах.

При необходимости функцию можно отключить.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    sharedPrerenderData: false,
  },
})

Важно: при включении опции ключ данных должен однозначно соответствовать этим данным. Например, при запросе данных страницы через useAsyncData задавайте ключ, однозначно идентифицирующий данные. (useFetch делает это автоматически.)

// Так делать небезопасно на динамической странице (напр. `[slug].vue`): slug влияет на данные,
// но Nuxt этого не видит, так как ключ от него не зависит.
const route = useRoute()
const { data } = await useAsyncData(async (_nuxtApp, { signal }) => {
  return await $fetch(`/api/my-page/${route.params.slug}`, { signal })
})
// Используйте ключ, однозначно идентифицирующий запрашиваемые данные.
const { data } = await useAsyncData(route.params.slug, async (_nuxtApp, { signal }) => {
  return await $fetch(`/api/my-page/${route.params.slug}`, { signal })
})

clientNodeCompat

При включении Nuxt полифиллит импорты Node.js в клиентской сборке с помощью unenv.

Чтобы глобалы вроде Buffer работали в браузере, их нужно подключать вручную.
import { Buffer } from 'node:buffer'

globalThis.Buffer ||= Buffer

scanPageMeta

Nuxt передаёт модулям на этапе сборки часть метаданных маршрута из definePageMeta (alias, name, path, redirect, props, middleware).

Работает только со статическими значениями или строками/массивами, не с переменными и условными присваиваниями. Подробнее: исходный issue.

По умолчанию метаданные страниц сканируются после регистрации всех маршрутов в pages:extend, затем вызывается хук pages:resolved.

При проблемах в проекте опцию можно отключить.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    scanPageMeta: false,
  },
})

cookieStore

Включает поддержку CookieStore для отслеживания изменений cookies (если браузер поддерживает) и обновления ref из useCookie.

По умолчанию опция включена; при необходимости её можно отключить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    cookieStore: false,
  },
})
Подробнее о CookieStore.

buildCache

Кэширует артефакты сборки Nuxt по хешу конфигурации и исходных файлов.

Учитываются только исходные файлы в srcDir и serverDir (части Vue и Nitro приложения).

По умолчанию отключено; при необходимости можно включить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    buildCache: true,
  },
})

При включении полная пересборка выполняется при изменении следующих файлов:

Directory structure
.nuxtrc
.npmrc
package.json
package-lock.json
yarn.lock
pnpm-lock.yaml
tsconfig.json
bun.lock
bun.lockb

Кроме того, любые изменения файлов в srcDir приводят к пересборке клиентского и серверного бандла Vue. Nitro пересобирается всегда (ведётся работа над тем, чтобы Nitro мог объявлять кэшируемые артефакты и их хеши).

Хранится не более 10 кэш-архивов.

checkOutdatedBuildInterval

Интервал (в мс) проверки наличия новых сборок. Не действует, если experimental.appManifest отключён.

Значение false отключает проверку.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    checkOutdatedBuildInterval: 3600000, // 1 hour, or false to disable
  },
})

extraPageMetaExtractionKeys

Макрос definePageMeta() позволяет собирать метаданные страниц на этапе сборки. Nuxt предоставляет фиксированный набор поддерживаемых ключей для внутренних возможностей: редиректы, алиасы страниц, кастомные пути.

Эта опция задаёт дополнительные ключи для извлечения из метаданных страницы при использовании scanPageMeta.

<script lang="ts" setup>
definePageMeta({
  foo: 'bar',
})
</script>
export default defineNuxtConfig({
  experimental: {
    extraPageMetaExtractionKeys: ['foo'],
  },
  hooks: {
    'pages:resolved' (ctx) {
      // ✅ foo is available
    },
  },
})

Модули могут получать из метаданных страницы дополнительные ключи в контексте сборки. При использовании в модуле рекомендуется также дополнить типы NuxtPage своими ключами.

Перед навигацией ожидается один кадр анимации, чтобы браузер успел перерисовать экран и зафиксировать действие пользователя.

Может снижать INP при навигации по предрендеренным маршрутам.

По умолчанию включено; при необходимости можно отключить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    navigationRepaint: false,
  },
})

normalizeComponentNames

Nuxt приводит автоматически сгенерированные имена компонентов Vue к полному имени, которое используется при автоимпорте.

При возникновении проблем опцию можно отключить.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    normalizeComponentNames: false,
  },
})

По умолчанию Vue назначает компоненту имя по имени файла, если имя не задано вручную.

Directory structure
├─ components/
├─── SomeFolder/
├───── MyComponent.vue

В этом случае имя компонента в Vue будет MyComponent. Для использования с <KeepAlive> или отображения в Vue DevTools нужно использовать это имя.

Для автоимпорта при этом потребуется имя SomeFolderMyComponent.

При включении experimental.normalizeComponentNames оба имени совпадают: Vue генерирует имя компонента по правилам именования Nuxt.

normalizePageNames

Имена компонентов страниц приводятся в соответствие с именами маршрутов. На компоненты страниц устанавливается свойство __name, чтобы Vue мог корректно различать их в <KeepAlive> по имени.

По умолчанию Vue назначает компонентам имена по имени файла: например, у pages/foo/index.vue и pages/bar/index.vue будет имя index. Из-за этого фильтрация <KeepAlive> по имени ненадёжна — несколько страниц имеют одно имя.

При включённом normalizePageNames компоненты страниц получают имена по маршруту (например, foo и bar), поэтому можно использовать <KeepAlive> с include/exclude без ручного добавления defineOptions({ name: '...' }) на каждой странице.

Опция включается при future.compatibilityVersion: 5 и выше; при необходимости её можно отключить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    normalizePageNames: false,
  },
})
app.vue
<template>
  <NuxtPage :keepalive="{ include: ['foo'] }" />
</template>

spaLoadingTemplateLocation

При рендере страницы только для клиента (ssr: false) при необходимости показывается экран загрузки из ~/spa-loading-template.html.

Значение within встраивает шаблон так:

<div id="__nuxt">
  <!-- spa loading template -->
</div>

Либо шаблон можно вывести рядом с корнем приложения Nuxt, задав значение body:

<div id="__nuxt"></div>
<!-- spa loading template -->

Это уменьшает белую вспышку при гидрации страницы только для клиента.

browserDevtoolsTiming

Включает маркеры производительности для хуков Nuxt в инструментах разработчика. Их можно смотреть во вкладке Performance в браузерах на Chromium — удобно для отладки и оптимизации.

По умолчанию включено в режиме разработки; при необходимости можно отключить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    browserDevtoolsTiming: false,
  },
})
Детали реализации в PR #29922.
Подробнее о Chrome DevTools Performance API.

debugModuleMutation

Записывает изменения nuxt.options в контексте модулей — помогает отлаживать правки конфигурации при инициализации Nuxt.

По умолчанию включено при включённом режиме debug; при необходимости можно отключить.

Чтобы включить явно:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    debugModuleMutation: true,
  },
})
Детали реализации в PR #30555.

lazyHydration

Включает стратегии гидрации для компонентов <Lazy>: гидрация откладывается до момента необходимости, что улучшает производительность.

По умолчанию включено; при необходимости можно отключить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    lazyHydration: false,
  },
})
Подробнее о ленивой гидрации.

templateImportResolution

Отключает разрешение импортов в шаблонах Nuxt относительно пути модуля, добавившего шаблон.

По умолчанию Nuxt разрешает импорты в шаблонах относительно модуля, который их добавил. Значение false отключает это поведение — может пригодиться при конфликтах разрешения в некоторых средах.

По умолчанию включено; при необходимости можно отключить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    templateImportResolution: false,
  },
})
Детали реализации в PR #31175.

templateRouteInjection

По умолчанию объект маршрута из автоимпортируемого композабла useRoute() синхронизирован с текущей страницей в <NuxtPage>. Для экспортируемого vue-router'ом useRoute и для объекта $route в шаблонах Vue это не так.

При включении этой опции подмешивается миксин, который синхронизирует объект $route в шаблоне с управляемым Nuxt useRoute().

По умолчанию включено; при необходимости можно отключить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    templateRouteInjection: false,
  },
})

decorators

Включает синтаксис декораторов во всём приложении Nuxt/Nitro.

При сборщике Vite (по умолчанию) декораторы обрабатываются через Babel и @babel/plugin-proposal-decorators. При webpack или rspack — через esbuild.

В TypeScript давно есть поддержка декораторов через compilerOptions.experimentalDecorators — это реализация до стандартизации TC39. Сейчас декораторы — предложение Stage 3, в TS 5.0+ поддерживаются без отдельной настройки (см. https://github.com/microsoft/TypeScript/pull/52582 и https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-beta/#decorators).

Включение experimental.decorators даёт поддержку предложения TC39, а не старой реализации TypeScript compilerOptions.experimentalDecorators.

До финального включения в стандарт JS возможны изменения.

Использование

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    decorators: true,
  },
})

При использовании сборщика Vite или серверной сборки Nitro потребуются дополнительные пакеты Babel в dev-зависимостях:

npm install -D @babel/plugin-proposal-decorators @babel/plugin-syntax-jsx
Nuxt предложит установить их автоматически, если они ещё не установлены.
app/app.vue
function something (_method: () => unknown) {
  return () => 'decorated'
}

class SomeClass {
  @something
  public someMethod () {
    return 'initial'
  }
}

const value = new SomeClass().someMethod()
// this will return 'decorated'

defaults

Задаёт опции по умолчанию для основных компонентов и композаблов Nuxt.

В будущем эти опции, вероятно, перенесут (например, в app.config или в каталог app/).

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    defaults: {
      nuxtLink: {
        componentName: 'NuxtLink',
        prefetch: true,
        prefetchOn: {
          visibility: true,
        },
      },
      useAsyncData: {
        deep: true,
      },
      useState: {
        resetOnClear: true,
      },
    },
  },
})

Опция useState.resetOnClear задаёт, сбрасывает ли clearNuxtState состояние в начальное значение (из функции init в useState) вместо undefined. По умолчанию true при compatibilityVersion: 5.

purgeCachedData

Очищать ли кэши Nuxt (static и asyncData) при навигации по маршрутам.

Nuxt автоматически очищает кэш из useAsyncData и nuxtApp.static.data. Это снижает утечки памяти и даёт актуальные данные при необходимости, но поведение можно отключить.

По умолчанию включено; при необходимости можно отключить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    purgeCachedData: false,
  },
})
Детали реализации в PR #31379.

prefetchPreloadTags

Если <NuxtLink> префетчится и у целевого маршрута включено извлечение payload (по умолчанию для пререндеренных и кэшируемых маршрутов), подсказки <link rel="preload">, которые целевая страница задала через useHead (или модули вроде <NuxtImg preload> из @nuxt/image), переносятся в текущий документ.

Перенесённые ссылки понижаются с rel="preload" до rel="prefetch", чтобы не конкурировать с критичными ресурсами текущей страницы. Переносятся только пользовательские теги head; preload чанков JS/CSS на этапе сборки уже обрабатываются отдельно в пайплайне префетча.

Флаг по умолчанию выключен: вместе с prefetchOn: 'visibility' (значение по умолчанию у <NuxtLink>) это может запустить много кросс-маршрутных префетчей сразу. Включайте его, когда уверены, что preload на целевых страницах стоит переносить для ссылок, с которыми обычно взаимодействуют пользователи.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    prefetchPreloadTags: true,
  },
})
Мотивация описана в issue #34953.

granularCachedData

Вызывать ли и использовать ли результат getCachedData при обновлении данных для useAsyncData и useFetch (через watch, refreshNuxtData() или ручной вызов refresh()).

По умолчанию включено; при необходимости можно отключить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    granularCachedData: false,
  },
})
Детали реализации в PR #31373.

headNext

Оптимизации для работы с head:

  • Подключается плагин capo.js для более эффективного рендера тегов в <head>.
  • Используется плагин hash hydration для снижения нагрузки при начальной гидрации.

По умолчанию включено; при необходимости можно отключить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    headNext: false,
  },
})

pendingWhenIdle

Для useAsyncData и useFetch: должно ли pending быть true, пока загрузка данных ещё не начата.

По умолчанию отключено; при необходимости можно включить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    pendingWhenIdle: true,
  },
})

entryImportMap

По умолчанию Nuxt повышает стабильность чанков, разрешая entry-чанк бандла через import map.

В начало тега <head> добавляется import map:

<script type="importmap">{"imports":{"#entry":"/_nuxt/DC5HVSK5.js"}}</script>

В чанках скриптов, выдаваемых Vite, импорты идут из #entry. Изменения entry-файла не инвалидируют остальные неизменённые чанки.

Nuxt автоматически отключает эту возможность, если в vite.build.target указан браузер без поддержки import maps или если vite.build.rolldownOptions.output.entryFileNames задан без [hash].

Отключить эту возможность можно так:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    entryImportMap: false,
  },
  // или лучше задать нужный target в vite — Nuxt его учтёт
  vite: {
    build: {
      target: 'safari13',
    },
  },
})

typescriptPlugin

Включает улучшенный опыт разработки с TypeScript через модуль @dxup/nuxt.

Экспериментальный плагин даёт лучшую интеграцию TypeScript и инструменты для удобной работы с TypeScript в Nuxt.

По умолчанию отключено; при необходимости можно включить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    typescriptPlugin: true,
  },
})
Для использования нужно:
  • Иметь typescript в зависимостях
  • Настроить VS Code на использование TypeScript из workspace (см. документацию VS Code)
Подробнее о @dxup/nuxt.

ssrStreaming

Включает потоковый SSR (streaming), чтобы значительно улучшить Time to First Byte (TTFB). Когда опция включена, сервер сразу отправляет HTML-оболочку (включая <head>, стили, подсказки предзагрузки и входные скрипты), а затем постепенно передаёт отрендеренное тело страницы через renderToWebStream из Vue.

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    ssrStreaming: true,
  },
})

Стриминг автоматически отключается для ботов и поисковых роботов (Googlebot, Bingbot и т.д.), чтобы поисковые системы получали полностью отрендеренный HTML и SEO не страдало. Шаблон по умолчанию охватывает только индексирующих роботов; Lighthouse и другие инструменты аудита намеренно под него не попадают, чтобы синтетические измерения отражали тот же потоковый ответ, что и у реальных пользователей. Регулярное выражение для определения ботов можно настроить:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    ssrStreaming: {
      botRegex: /googlebot|bingbot|my-internal-crawler/i,
    },
  },
})

Стримингом также можно управлять для каждого маршрута через routeRules:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    ssrStreaming: true,
  },
  routeRules: {
    '/no-stream/**': { streaming: false },
  },
})
Автоматический откат к обычному (не потоковому) рендерингу. Стриминг фиксирует статус и заголовки ответа сразу после отправки оболочки, что несовместимо с функциями, которым нужно менять ответ уже после рендеринга. Запросы, попадающие под любое из условий ниже, не стримятся: они используют буферизованный рендерер либо сразу отдают редирект или ответ с ошибкой:
  • routeRules задаёт для маршрута noScripts, cache, isr, swr, redirect или streaming: false
  • Маршруты с ssr: false (уже отрендеренные как SPA)
  • Боты и поисковые роботы (управляется через botRegex)
  • Пререндеренные маршруты (nuxi generate)
  • Серверные редиректы navigateTo() из плагинов, middleware или setup страницы
  • Фатальные ошибки при первичном рендеринге (до отправки оболочки)
Статус и заголовки ответа нужно задать до отправки оболочки. Стриминг фиксирует HTTP-статус и заголовки уже с первым байтом, поэтому всё, что меняет ответ позже, до клиента не доходит. Это свойство самого стриминга, а не баг Nuxt.Граница — момент отправки оболочки:
  • Доходит до клиента: изменения из плагинов Nuxt и Nitro, которые полностью отрабатывают до начала рендеринга.
  • Отбрасывается: вызовы setResponseStatus(), useResponseHeader(), записи useCookie() и вызовы h3 setHeader()/appendResponseHeader() во время рендеринга компонентов (в том числе после await в route middleware или <script setup>), поскольку это происходит, когда оболочка уже ушла по сети.
Чтобы изменение ответа сохранилось, перенесите его в плагин или отключите стриминг для маршрута:
  • routeRules: { '/path': { streaming: false } } — статически, для маршрута.
  • Хук render:route с ctx.prefersStream = false — в рантайме, для отдельного запроса (например, для маршрутов, которые условно возвращают 404).
В режиме разработки обработчик стриминга выводит предупреждение с перечислением отброшенных изменений и маршрута, чтобы такие случаи не проходили незаметно.
Если ошибка возникает во время стриминга, когда HTTP-статус уже зафиксирован, устанавливается payload.error, а закрывающие теги всё равно отправляются как корректный документ — клиент подхватывает ошибку при гидратации и рендерит страницу ошибки. Ошибки, выброшенные до отправки оболочки, попадают в буферизованный рендерер ошибок с правильным кодом статуса.
Стили маршрута стримятся, JS-подсказки — только для входного чанка. Оболочка отправляется до рендеринга маршрута, поэтому её <head> несёт только стили и подсказки входного чанка. Как только рендеринг зарегистрирует модули страницы и макета, их CSS стримится сразу за оболочкой (встроенным <style> при включённом inlineStyles, иначе ссылками на стили), поэтому стили страницы, макета и async-компонентов верхнего уровня приходят до отрисовки тела (вложенные async-компоненты — отдельный случай FOUC, о нём ниже). JS-чанки конкретного маршрута из оболочки не предзагружаются; браузер находит их после разбора входного скрипта. Стриминг улучшает TTFB на любом маршруте; выигрыш по LCP наибольший на маршрутах, чей JS пересекается с входным чанком.
Островки компонентов (islands) совместимы со стримингом. Содержимое слотов островков и компоненты с выборочной клиентской гидратацией (nuxt-client) обычно вставляются в HTML отдельным проходом после рендеринга, что невозможно, когда тело уже прошло мимо якорей островков в потоке. Вместо этого рендерер выводит каждый teleport островка как инертный <template> в конце документа и перемещает его на место inline-скриптом, который выполняется до гидратации. Для кода приложения это прозрачно. Исключение — приложения, собранные с features.noScripts и компонентами-островками: они откатываются к буферизованному рендереру, поскольку скрипт перемещения не может выполниться.

Хуки модулей

Модули участвуют в потоковом ответе через существующий хук render:html (теперь со флагом streaming: true во втором аргументе), а также через хук принятия решения на каждый запрос и два хука только для стриминга:

  • render:route срабатывает один раз на запрос перед началом рендеринга, для любого рендеринга (со стримингом или без). Прочитайте ctx.canStream, чтобы узнать, возможен ли стриминг для маршрута, и задайте ctx.prefersStream = false, чтобы принудительно использовать буферизованный рендеринг для этого запроса — например, по куке, статусу авторизации или A/B-группе. Рендерер стримит только при canStream && prefersStream. Это рантайм-обходной путь для статической конфигурации routeRules / botRegex.
  • render:html срабатывает один раз, до отправки оболочки, со флагом streaming: true во втором аргументе. Изменения htmlAttrs, head, bodyAttrs и bodyPrepend доходят по сети. Изменения body/bodyAppend отбрасываются, поскольку тело вот-вот начнёт стримиться (в режиме разработки выводится предупреждение). Модули, которые меняют только поля head (внедрение CSP, OG-теги, meta для аналитики), работают со стримингом без изменений кода.
  • render:html:chunk срабатывает для каждого чанка, произведённого рендерером, перед постановкой его в очередь. Меняйте ctx.chunk: Uint8Array, чтобы преобразовать байты (например, внедрить nonce); читайте ctx.index, чтобы отличить первый чанк от последующих.
  • render:html:close срабатывает после завершения потока тела, перед закрывающими тегами. Меняйте ctx.bodyAppend: string[], чтобы вставить финальную разметку (теги аналитики в конце body, серверные отладочные виджеты и т.д.).
// modules/streaming-csp/src/runtime/server-plugin.ts
import { defineNitroPlugin } from '#imports'

export default defineNitroPlugin((nitro) => {
  nitro.hooks.hook('render:html', (ctx, { event }) => {
    const nonce = event.context.cspNonce
    if (!nonce) { return }
    // Работает и для стриминга (до оболочки), и для буферизованного пути (после рендеринга).
    for (let i = 0; i < ctx.head.length; i++) {
      ctx.head[i] = ctx.head[i].replace(/<script(?![^>]*\snonce=)/g, `<script nonce="${nonce}"`)
    }
  })
})
CSP nonce. Потоковый рендерер выводит несколько inline-скриптов и стилей в обход unhead: очередь bootstrap, IIFE, вставки head от suspense, перемещение teleport островков и блоки <style> маршрута. Если на отрендеренных скриптах head есть nonce, рендерер автоматически переиспользует его на всех них, поэтому строгая политика script-src/style-src 'nonce-…' не блокирует стриминг. Модулю достаточно проставить nonce на скриптах head (как выше); хук render:html:chunk остаётся доступным для проставления nonce на скриптах, которые компоненты рендерят в тело.
FOUC в режиме разработки для стилей SFC. В разработке Vite отдаёт блоки <style> из SFC как JavaScript-модули, которые внедряют стили на клиенте после выполнения модуля, без соответствующего <link> в оболочке. При стриминге браузер начинает отрисовывать переданный DOM ещё до выполнения этих модулей-инжекторов, поэтому стили из SFC на короткое время мигают без оформления.Обходной путь: вынесите критичные для отрисовки стили в глобальный CSS-файл, подключённый через css: ['~/assets/main.css']. Глобальные CSS-файлы выводятся как <link rel="stylesheet"> в <head> оболочки и применяются до того, как начнёт стримиться тело. Блоки <style> в SFC по-прежнему подходят для стилей уровня компонента, которые не влияют на первую отрисовку.Продакшен-сборки извлекают все стили в настоящие CSS-файлы (или встраивают через features.inlineStyles), поэтому это касается только nuxt dev. Проверяйте потоковую отрисовку через nuxt build && nuxt preview.
FOUC в продакшене для вложенных async-компонентов. Рендерер встраивает CSS маршрута в чанк, отправляемый сразу за оболочкой. Он может встроить стили только для тех компонентов, чьи модули уже зарегистрированы к этому моменту: страницы, макета и любого async-компонента, размещённого непосредственно внутри границы <Suspense> (Vue создаёт их сразу при старте рендеринга).Async-компонент, который рендерится внутри другого async-компонента, создаётся только после разрешения родителя — уже после того, как первый чанк ушёл в поток. Его <style> из SFC не попадает в чанк стилей после оболочки и вместо этого выводится в закрывающем HTML, за DOM самого компонента. Браузер отрисовывает такой компонент без стилей, пока не придёт финальный чанк.Чтобы избежать этого, держите критичные для отрисовки стили вне глубоко вложенных async-компонентов:
  • Выносите стили, влияющие на первую отрисовку, в глобальный CSS-файл (css: ['~/assets/main.css']); они попадают в <head> оболочки.
  • Используйте утилитарные классы (Tailwind, UnoCSS): утилитарный CSS живёт во входном файле стилей, а не в блоках <style> отдельных компонентов.
  • Держите async-компоненты с критичным для отрисовки <style> прямо под границей <Suspense>, а не вложенными за другим async-родителем.
  • Либо отключите стриминг для маршрута через routeRules: { '/path': { streaming: false } }.
Некритичные для отрисовки scoped-стили на вложенных async-компонентах — не проблема: короткое мигание важно только для контента над сгибом (above the fold).