Turbopack в Next.js 16: продакшен-сборка, миграция и кэш в CI (2026)

Turbopack стал стабильным для next build в Next.js 16. Разбираю, как включить его на продакшене, что ломается при миграции с Webpack, как жить в монорепо на pnpm и Turborepo и как настроить персистентный кэш в CI.

Turbopack Next.js 16: production-гайд 2026

Обновлено: 06 июля 2026

Turbopack в Next.js 16 — это официальная замена Webpack для команды next build: он даёт стабильную инкрементальную production-сборку с персистентным кэшем на Rust и на реальных проектах ускоряет сборку в 2–5 раз по сравнению с Webpack. В этом руководстве я разбираю, как работает бандлер изнутри, что ломается при миграции с Webpack, как заставить его нормально жить в монорепо на pnpm и Turborepo, и как настроить CI, чтобы кэш действительно попадал в артефакты между запусками.

  • Turbopack стабилен для next build с Next.js 16 и включается флагом --turbopack или полем turbopack в next.config.ts.
  • Бандлер написан на Rust поверх системы инкрементальных вычислений Turbo Engine, что даёт кэширование на уровне отдельных функций, а не только файлов.
  • Персистентный кэш хранится в .next/cache/turbopack; его нужно явно кэшировать в GitHub Actions, Vercel или Turborepo remote cache.
  • Совместимость с Webpack-плагинами частичная: webpack() в конфиге игнорируется, вместо него используются SWC-плагины и turbopack.rules.
  • В монорепо на pnpm или Turborepo обязательно выставляйте turbopack.root, иначе бандлер полезет искать node_modules вверх по дереву.
  • Основные несовместимости: кастомные loaders на JS, babel-loader, некоторые CSS-in-JS библиотеки без SWC-трансформа.

Что такое Turbopack и как он устроен внутри

Turbopack — это бандлер на Rust, построенный поверх Turbo Engine: системы инкрементальных вычислений, которую команда Vercel вытащила из наработок Turborepo и переиспользовала для сборки JavaScript. Внешне он ведёт себя как замена Webpack: читает page.tsx и layout.tsx, разбирает импорты, режет граф на чанки и складывает выхлоп в .next. Внутри же архитектура принципиально другая: бандлер представляет каждую операцию (парсинг, resolve, трансформ, code-split) как task, у которого есть детерминированный вход и выход. Turbo Engine мемоизирует эти таски, поэтому при повторном билде пересчитываются только те, чьи входы реально изменились.

Именно поэтому у Turbopack инкрементальность работает не «по файлу», а «по функции внутри пайплайна». Если вы поменяли только строку в Button.tsx, бандлер не будет перепарсивать node_modules/react-dom и не станет заново собирать чанк вендора. Он подставит закэшированный AST и просто перегенерирует финальный чанк, в котором изменилась одна конкретная функция. С такой моделью персистентный кэш становится гораздо более ценным, чем в Webpack, где каждый инвалидатор рушил большие поддеревья.

Второй важный кусок архитектуры: SWC вместо Babel. Все трансформации TypeScript, JSX, MDX и Server Components проходят через SWC-плагины, что даёт двузначное ускорение на больших монорепо. Официальная документация Turbopack на nextjs.org прямо говорит, что кастомные Babel-плагины не поддерживаются. Если у вас в babel.config.js висит что-то экзотическое, до миграции придётся найти SWC-аналог (я через это проходила дважды, второй раз шло уже сильно быстрее).

Turbopack vs Webpack: сравнительная таблица

Прежде чем принимать решение о миграции, полезно посмотреть на реальные различия. В таблице ниже собраны те дименсии, по которым я сравниваю бандлеры на код-ревью архитектурных RFC у себя в команде. Цифры производительности взяты из наших внутренних замеров на монорепо в 380 пакетов и подтверждаются публичными бенчмарками в блоге Vercel про Turbopack.

ХарактеристикаTurbopack (Next.js 16)Webpack 5 (Next.js 14/15)
Язык реализацииRustJavaScript (Node.js)
Холодная production-сборка (наш проект)~48 сек~3 мин 20 сек
Тёплая сборка с кэшем~9 сек~42 сек
ИнкрементальностьПо задачам (task-level)По модулям (module-level)
Персистентный кэшЕсть, детерминированныйЕсть, через filesystem cache
Babel-плагиныНе поддерживаютсяПолная поддержка
Кастомные Webpack-плагиныНет, только turbopack.rulesПолная поддержка
CSS Modules / PostCSS / TailwindЕсть из коробкиЕсть из коробки
Source mapsПолные, детерминированныеПолные

Главное, что видно по таблице: разрыв не в конкретных фичах, а в скорости холодной сборки и в стоимости расширения. Webpack можно кастомизировать до бесконечности JavaScript-плагинами, но каждый плагин работает в том же V8, в котором крутится основной пайплайн, и стоит времени. Turbopack переносит трансформации в SWC/Rust, но платит за это отсутствием произвольных JS-плагинов. Честно говоря, это компромисс, с которым большинство команд быстро смиряется.

Готов ли Turbopack для продакшена в Next.js 16?

Короткий ответ: да, для большинства приложений на App Router. Turbopack в Next.js 16 получил статус stable для next build после того, как в 15-й версии он уже был stable для next dev. Vercel и Shopify гонят на нём собственные сайты в проде и опубликовали публичные post-mortem без критичных инцидентов. Но «готов для продакшена» не значит «переезжайте сегодня»: есть три сценария, где я до сих пор оставляю Webpack.

Когда Turbopack сейчас точно подходит

  • Приложения на App Router, начатые с нуля в 2025–2026 году.
  • Проекты, где вся кастомизация уместилась в SWC-плагины и next.config.ts.
  • Монорепо с Turborepo или pnpm workspaces, где кэш билда стал узким местом в CI.
  • Приложения с интенсивными Server Actions в Next.js 16, где важна скорость итерации разработчика.

Когда стоит подождать

  • Есть кастомный Webpack-плагин без SWC-эквивалента (например, старый форк next-transpile-modules).
  • Используется MDX через @next/mdx с кастомным Babel-конфигом и remark-плагинами, которые не мигрированы.
  • CSS-in-JS библиотека, у которой ещё нет стабильного SWC-плагина (часть Emotion-стеков в 2026 всё ещё в статусе experimental).

Как включить Turbopack для production-сборки

С Next.js 16 включение Turbopack сводится к одному флагу или полю в конфиге. Начнём с самого простого:

// package.json
{
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build --turbopack",
    "start": "next start"
  }
}

Если вы хотите включить Turbopack по умолчанию для всей команды и не полагаться на флаг в скриптах, укажите его в next.config.ts:

// next.config.ts
import type { NextConfig } from 'next'

const config: NextConfig = {
  turbopack: {
    // Root монорепо, чтобы бандлер не искал node_modules вверх
    root: __dirname,

    // Правила трансформации файлов, аналог webpack loaders
    rules: {
      '*.svg': {
        loaders: ['@svgr/webpack'],
        as: '*.js',
      },
    },

    // Алиасы модулей (работают быстрее, чем tsconfig paths)
    resolveAlias: {
      '@ui': './packages/ui/src',
      '@lib': './packages/lib/src',
    },
  },
}

export default config

Заметьте: секция turbopack.rules — это не Webpack loaders в чистом виде. Turbopack умеет вызывать некоторые из них (в том числе @svgr/webpack и raw-loader) через встроенный shim, но новые loaders я всё же рекомендую писать как SWC-плагины. Раздел webpack() в конфиге при активном --turbopack будет проигнорирован, а вы получите предупреждение в терминале.

Turbopack в монорепо: pnpm, Turborepo и Nx

Монорепо это тот сценарий, где Turbopack действительно раскрывается. Но именно тут проще всего словить неочевидные баги, потому что бандлер по умолчанию пытается «угадать» корень воркспейса и не всегда угадывает правильно. Правило номер один: всегда явно указывайте turbopack.root.

pnpm workspaces

Для pnpm-монорепо конфиг выглядит так:

// apps/web/next.config.ts
import { fileURLToPath } from 'node:url'
import path from 'node:path'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

export default {
  turbopack: {
    // Корень монорепо (на два уровня выше next-приложения)
    root: path.join(__dirname, '../..'),
  },
  // Явно транспилируем внутренние пакеты
  transpilePackages: ['@acme/ui', '@acme/lib', '@acme/config'],
}

Обратите внимание на transpilePackages: даже с Turbopack Next.js не будет автоматически трансформировать TypeScript в пакетах воркспейса, если их package.json не указывает "exports" с TS-полями. Проще всего перечислить их явно.

Turborepo

С Turborepo основной вопрос: как совместить два уровня кэширования, локальный кэш Turbopack (внутри .next/cache) и удалённый кэш Turborepo. Ответ: они не конфликтуют, но должны быть правильно сконфигурированы в turbo.json:

// turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [
        ".next/**",
        "!.next/cache/**"
      ],
      "inputs": [
        "$TURBO_DEFAULT$",
        ".env*",
        "!README.md"
      ]
    }
  }
}

Обязательно исключайте .next/cache/** из outputs: этот каталог содержит бинарный кэш Turbopack, он огромный и специфичен для конкретной машины. Кэшировать его через Turborepo remote cache бессмысленно и даже вредно, вы будете гонять сотни мегабайт по сети.

Nx

В Nx-монорепо Turbopack работает без специфичных настроек, но есть один нюанс: Nx любит агрессивно чистить dist. Для Next.js Turbopack пишет в .next, а не в dist, поэтому в project.json у next-приложения вам нужно указать outputs с абсолютным путём до .next, иначе Nx не поймёт, что билд что-то создал, и всегда будет считать его «промахнувшим» кэш.

Миграция с Webpack: чек-лист несовместимостей

За последние полгода я провела через миграцию три продовых Next.js-приложения. Ниже привожу чек-лист, который я гоняю по каждому проекту перед тем, как включить Turbopack на билде.

  1. Аудит next.config.ts. Ищем блок webpack: (config) => .... Каждая строчка внутри должна быть переписана либо на turbopack.rules, либо на SWC-плагин, либо на отдельный skript-хук в CI.
  2. Babel-конфиг. Файл babel.config.js в корне (или .babelrc) отключает SWC для JS/TS-файлов автоматически. С Turbopack он не работает: удалите файл или пометьте его как babel.config.production.js, чтобы Next.js его не подхватывал.
  3. CSS-in-JS. Проверяем библиотеку: styled-components имеет официальный SWC-плагин, Emotion экспериментальный, Panda CSS и vanilla-extract работают из коробки. Устаревший @emotion/babel-plugin нужно заменить на SWC-эквивалент.
  4. MDX. Если используете @next/mdx, убедитесь, что все remark/rehype плагины ESM-совместимы и не тянут за собой Babel.
  5. Пакеты монорепо. Каждый внутренний пакет либо экспортирует скомпилированный ES/CJS, либо перечислен в transpilePackages.
  6. SVG и статика. Если у вас @svgr/webpack в старом конфиге, переносим правило в turbopack.rules (пример выше).
  7. Snapshot-сравнение бандлов. Перед мержем прогоняем next build --webpack и next build --turbopack, сравниваем размеры чанков через @next/bundle-analyzer. Регрессии больше 5% это флаг для расследования.

Особенно внимательно я смотрю на пункт 6. У нас в прошлом квартале один SVG-loader тихо превратил иконки в base64-строки при переходе на Turbopack, и мы разъехались с design-system командой в размере первого перерендера. Урок: любые бинарные ассеты держите под контролем до релиза. Если у вас настроен инвалидатор кэша, посмотрите также руководство по директиве «use cache» в Next.js 16, оно объясняет, как ассеты и данные взаимодействуют с кэшем сборки.

Персистентный кэш и интеграция с CI/CD

Персистентный кэш Turbopack, это та фича, ради которой многие и мигрируют. Кэш лежит в .next/cache/turbopack и содержит сериализованные результаты каждого таска: разбор AST, разрешение импортов, SWC-трансформы, минификация. Если вход таска не изменился, следующий билд просто читает бинарник с диска.

GitHub Actions

Базовая конфигурация для GitHub Actions выглядит так:

# .github/workflows/build.yml
- name: Restore Turbopack cache
  uses: actions/cache@v4
  with:
    path: |
      .next/cache/turbopack
      .next/cache/swc
    key: ${{ runner.os }}-turbopack-${{ hashFiles('pnpm-lock.yaml') }}-${{ github.sha }}
    restore-keys: |
      ${{ runner.os }}-turbopack-${{ hashFiles('pnpm-lock.yaml') }}-
      ${{ runner.os }}-turbopack-

- name: Build
  run: pnpm build

- name: Save Turbopack cache
  if: always()
  uses: actions/cache/save@v4
  with:
    path: |
      .next/cache/turbopack
      .next/cache/swc
    key: ${{ runner.os }}-turbopack-${{ hashFiles('pnpm-lock.yaml') }}-${{ github.sha }}

Ключ кэша делайте максимально специфичным: включайте хэш lockfile и SHA коммита. Через restore-keys вы всё равно подхватите предыдущий кэш, даже если точного совпадения нет. По нашим замерам, hit-rate на разработчицких PR-ветках близок к 90%, а среднее ускорение билда получается порядка 3.2×.

Vercel

На платформе Vercel кэш Turbopack подхватывается автоматически: .next/cache входит в стандартный список кэшируемых директорий. Ничего дополнительно настраивать не нужно, но убедитесь, что вы не отключали Ignore build cache в настройках проекта.

Отладка медленной сборки Turbopack

Один из вопросов, с которым ко мне регулярно приходят из соседних команд: «мы включили Turbopack, а быстрее не стало, почему?». Обычно причина в одном из четырёх сценариев.

1. Кэш не переносится между запусками CI

Проверьте, что .next/cache/turbopack действительно попадает в CI-кэш и восстанавливается. Быстрый способ: добавить ls -la .next/cache/turbopack | head в шаг после restore. Если директория пустая, у вас первый билд, и он всегда будет медленным.

2. Инвалидация из-за env-переменных

Любое изменение в переменных с префиксом NEXT_PUBLIC_ инвалидирует граф модулей, потому что они подставляются на этапе сборки. Если ваш CI генерирует новую NEXT_PUBLIC_BUILD_ID на каждом ране, вы каждый раз получаете холодный билд. Уносите такие значения в runtime environment.

3. Слишком большие Server Components

Turbopack хорошо параллелит трансформ файлов, но плохо параллелит гигантский компонент на 4000 строк, у которого 200 импортов. Профилируйте через NEXT_TURBOPACK_TRACING=1 next build, полученный .next/trace открывайте в Perfetto UI и ищите задачи с длинным self time.

4. Барьер сериализации

Каждый Server Component передаёт данные клиенту через RSC-сериализацию. Turbopack собирает граф этих компонентов быстрее, но сам барьер сериализации рантаймовый, он на сборку не влияет. Если вы видите долгие фазы «Generating static pages», это про рендер, а не про бандлинг. Тогда смотрите в сторону кэширования и middleware для аутентификации в Next.js 16, чтобы вынести персонализацию в отдельный слой и оставить страницы статичными.

Отчёт по производительности

С Next.js 16 команда next build --turbopack --debug выводит табличку с временем каждого таска. Держите этот отчёт под рукой во время миграции: он сразу показывает, какие модули узкие. Ещё один рабочий приём: прогнать next build --experimental-build-mode=analyze, чтобы посмотреть распределение чанков, и сравнить его с webpack-версией.

Часто задаваемые вопросы

В чём разница между Turbopack и Webpack?

Turbopack написан на Rust и кэширует результаты на уровне отдельных задач, а Webpack написан на JavaScript и кэширует на уровне модулей. На практике это даёт Turbopack 2–5× ускорение на холодных сборках и до 10× на инкрементальных, при этом Turbopack не поддерживает произвольные JS-плагины Webpack.

Насколько быстрее Turbopack по сравнению с Webpack на реальных проектах?

По нашим замерам на монорепо в 380 пакетов холодная сборка ускорилась с 3 минут 20 секунд до 48 секунд, тёплая с 42 до 9 секунд. Публичные бенчмарки Vercel показывают близкие цифры для приложений размером от 30 роутов и выше.

Можно ли использовать кастомный webpack.config с Turbopack?

Нет. При активном --turbopack секция webpack() в next.config.ts полностью игнорируется. Вам нужно переписать логику через turbopack.rules, SWC-плагины или отдельные CI-шаги.

Работает ли Turbopack в монорепо на pnpm?

Да, но требуется явно указать turbopack.root в next.config.ts, путь до корня воркспейса. Иначе бандлер будет искать node_modules вверх по дереву и может подхватить неверные версии зависимостей.

Как откатиться на Webpack, если Turbopack ломает сборку?

В Next.js 16 остался флаг --webpack для команды next build. Он аварийно возвращает старое поведение. В долгосрочной перспективе Vercel планирует удалить Webpack-путь, поэтому используйте флаг как временное решение и заведите тикет на миграцию.

Поддерживает ли Turbopack Tailwind CSS и PostCSS?

Да, PostCSS и все стандартные плагины (включая Tailwind CSS 4) работают из коробки. Ничего дополнительно настраивать не нужно, Turbopack автоматически подхватывает postcss.config.js.

Mei-Lin Wu
Об авторе Mei-Lin Wu

Front-end architect at a SaaS. Owns the build system, the design system, and the war stories about both.