Почему Next.js 16 — важный релиз
Next.js 16 закрепляет все экспериментальные фичи, которые появились в версии 15, и делает их стабильными. Turbopack стал
сборщиком по умолчанию, конфигурация получила нативную поддержку TypeScript, а новые API вроде after() и forbidden()
упрощают типовые серверные сценарии. Если вы откладывали миграцию с 14-й версии — сейчас лучший момент.
В этой статье разберём каждое нововведение с примерами кода и дадим пошаговый план миграции с Next.js 15. Материал будет
полезен как разработчикам, которые только начинают работать с фреймворком, так и командам с production-проектами.
Если вам нужна профессиональная React/Next.js разработка, наша команда работает с Next.js с ранних версий
и уже мигрировала несколько крупных проектов на версию 16.
Turbopack — сборщик по умолчанию
Что изменилось
В Next.js 15 Turbopack был стабилен только для dev-режима и требовал флага --turbo. В 16-й версии Turbopack стал *
сборщиком по умолчанию* — и для next dev, и для next build. Webpack больше не используется, если вы явно не
переключитесь на него.
Ключевые цифры из бенчмарков Vercel:
| Метрика |
Webpack |
Turbopack |
Ускорение |
| Холодный старт dev-сервера |
8.2 сек |
1.9 сек |
4.3x |
| Fast Refresh (изменение компонента) |
350 мс |
85 мс |
4.1x |
| Production build (средний проект) |
62 сек |
18 сек |
3.4x |
| Обработка 10 000 модулей |
45 сек |
7 сек |
6.4x |
Как это выглядит
Никаких флагов больше не нужно — Turbopack работает из коробки:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}
Если в проекте есть кастомные Webpack-плагины, которые ещё не совместимы с Turbopack, можно временно вернуться на
Webpack:
{
"scripts": {
"dev": "next dev --webpack",
"build": "next build --webpack"
}
}
Конфигурация Turbopack
Turbopack поддерживает собственный блок настроек для кастомных лоадеров и алиасов:
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
turbopack: {
rules: {
"*.svg": {
loaders: ["@svgr/webpack"],
as: "*.js",
},
},
resolveAlias: {
underscore: "lodash",
},
},
};
export default nextConfig;
Обратите внимание: блок turbopack вынесен из experimental на верхний уровень конфигурации — это отражает его
стабильный статус.
Нативный next.config.ts
Конец эры .js и .mjs
Next.js 16 нативно поддерживает конфигурацию в TypeScript. Больше не нужно использовать JSDoc-аннотации или сторонние
обёртки:
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "export",
trailingSlash: true,
images: {
unoptimized: true,
},
};
export default nextConfig;
Файл обрабатывается напрямую — без предварительной компиляции в JS. Поддерживаются импорты из других .ts-файлов, что
позволяет выносить сложную логику конфигурации в отдельные модули.
Приоритет файлов
Если в проекте есть несколько файлов конфигурации, Next.js выбирает по приоритету:
next.config.ts (наивысший)
next.config.mjs
next.config.js
Рекомендуем мигрировать на .ts — вы получите автодополнение, проверку типов и защиту от опечаток в названиях опций.
API after() — код после ответа
Проблема
Типичная задача: после обработки запроса нужно выполнить побочные действия — записать аналитику, обновить кэш, отправить
уведомление. Раньше для этого использовали waitUntil() (Vercel-специфичный) или фоновые задачи вручную.
Решение
after() — стабильный API для выполнения кода после того, как ответ отправлен клиенту. Он работает в Server
Components, Server Actions, Route Handlers и Middleware:
// app/api/checkout/route.ts
import { after } from "next/server";
export async function POST(request: Request) {
const order = await processOrder(request);
// Этот код выполнится после отправки ответа клиенту
after(async () => {
await analytics.track("order_completed", { orderId: order.id });
await emailService.sendConfirmation(order.email);
await inventoryService.decrementStock(order.items);
});
return Response.json({ orderId: order.id, status: "success" });
}
Использование в Server Components
// app/product/[id]/page.tsx
import { after } from "next/server";
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const product = await getProduct(id);
// Записываем просмотр после отрисовки страницы
after(async () => {
await analytics.trackView("product", id);
});
return <ProductDetails product={product} />;
}
Ключевое преимущество: after() не задерживает Time to First Byte (TTFB). Клиент получает ответ сразу, а побочные
задачи выполняются в фоне.
forbidden() и unauthorized()
Новые помощники для авторизации
Next.js 16 добавляет два новых помощника, аналогичных notFound():
import { forbidden, unauthorized } from "next/navigation";
unauthorized() → вызывает рендер файла 401.tsx (HTTP 401)
forbidden() → вызывает рендер файла 403.tsx (HTTP 403)
Пример использования
// app/admin/page.tsx
import { forbidden, unauthorized } from "next/navigation";
import { getSession } from "@/lib/auth";
export default async function AdminPage() {
const session = await getSession();
if (!session) {
unauthorized(); // Пользователь не авторизован → 401.tsx
}
if (session.role !== "admin") {
forbidden(); // Нет прав доступа → 403.tsx
}
return <AdminDashboard />;
}
Создание страниц ошибок
Добавьте файлы 401.tsx и 403.tsx в нужные сегменты:
// app/401.tsx
export default function UnauthorizedPage() {
return (
<div>
<h1>Необходима авторизация</h1>
<p>Войдите в аккаунт, чтобы получить доступ к этой странице.</p>
<a href="/login">Войти</a>
</div>
);
}
// app/403.tsx
export default function ForbiddenPage() {
return (
<div>
<h1>Доступ запрещён</h1>
<p>У вас нет прав для просмотра этой страницы.</p>
</div>
);
}
Раньше для таких случаев приходилось выбрасывать исключения или редиректить вручную. Теперь это стандартная часть
фреймворка.
Partial Prerendering — стабильная версия
Что это
Partial Prerendering (PPR) позволяет одной странице быть частично статической и частично динамической. Статическая
оболочка отдаётся мгновенно из CDN, а динамические части стримятся по мере готовности.
В Next.js 15 PPR был экспериментальным. В 16-й версии он стал стабильным и включается инкрементально — для отдельных
маршрутов:
Как это работает
- При
next build Next.js рендерит страницу и находит границы <Suspense>
- Статическая часть сохраняется как HTML-оболочка
- Динамические части заменяются fallback-заглушками
- При запросе пользователя сервер отдаёт статический HTML мгновенно
- Динамические части стримятся и заменяют заглушки
// app/product/[id]/page.tsx
import { Suspense } from "react";
export const experimental_ppr = true;
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
return (
<main>
{/* Статическая часть — рендерится при сборке */}
<ProductInfo id={id} />
{/* Динамические части — стримятся при каждом запросе */}
<Suspense fallback={<ReviewsSkeleton />}>
<ProductReviews id={id} />
</Suspense>
<Suspense fallback={<RelatedSkeleton />}>
<RelatedProducts id={id} />
</Suspense>
</main>
);
}
Включение PPR
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
experimental: {
ppr: "incremental",
},
};
export default nextConfig;
Затем отметьте маршруты, которые должны использовать PPR:
export const experimental_ppr = true;
PPR лучше всего подходит для страниц с комбинацией статического и динамического контента: каталоги товаров, дашборды,
персонализированные лендинги. Подробнее об оптимизации производительности читайте в нашей статье Оптимизация Next.js: от
50 до 100 баллов PageSpeed.
Улучшения кэширования
Стабильный opt-in подход
В Next.js 15 кэширование fetch() стало opt-in вместо opt-out. В 16-й версии этот подход закреплён и расширен.
Появились более удобные утилиты для управления кэшем:
import { unstable_cache } from "next/cache";
// Кэширование произвольных функций (не только fetch)
const getCachedProducts = unstable_cache(
async (category: string) => {
return await db.product.findMany({ where: { category } });
},
["products"],
{ revalidate: 300, tags: ["products"] },
);
// Использование
const products = await getCachedProducts("electronics");
Тегированная ревалидация
Система тегов стала надёжнее и предсказуемее:
// Кэшируем с тегами
const product = await fetch(`https://api.example.com/products/${id}`, {
next: { tags: [`product-${id}`, "products"] },
});
// Инвалидируем конкретный продукт
import { revalidateTag } from "next/cache";
revalidateTag(`product-${id}`);
// Или все продукты разом
revalidateTag("products");
Клиентский Router Cache
Поведение клиентского кэша маршрутов осталось opt-in, как в 15-й версии. Если нужно включить кэширование навигации:
// next.config.ts
const nextConfig: NextConfig = {
experimental: {
staleTimes: {
dynamic: 30,
static: 180,
},
},
};
Улучшения Server Actions
Безопасность и производительность
Server Actions в Next.js 16 стали ещё безопаснее. Фреймворк автоматически шифрует идентификаторы действий и создаёт
уникальные эндпоинты при каждой сборке. Это предотвращает воспроизведение старых запросов после деплоя.
useActionState — стабильный API
Хук useActionState из React 19, который пришёл на смену useFormState, полностью стабилен:
"use client";
import { useActionState } from "react";
import { createPost } from "./actions";
export function CreatePostForm() {
const [state, formAction, isPending] = useActionState(createPost, null);
return (
<form action={formAction}>
<input name="title" placeholder="Заголовок поста" required />
{state?.error && <p className="text-red-500">{state.error}</p>}
<button type="submit" disabled={isPending}>
{isPending ? "Создание..." : "Создать пост"}
</button>
</form>
);
}
Улучшенная обработка ошибок
Server Actions интегрированы с Error Boundaries. Ошибки в серверных действиях корректно перехватываются ближайшим
error.tsx:
// app/actions.ts
"use server";
import { revalidatePath } from "next/cache";
export async function createPost(formData: FormData) {
const title = formData.get("title") as string;
if (!title || title.length < 3) {
return { error: "Заголовок должен быть не менее 3 символов" };
}
await db.post.create({ data: { title } });
revalidatePath("/posts");
return { success: true };
}
Руководство по миграции с Next.js 15 на 16
Шаг 1: Обновление зависимостей
npm install next@16 react@19 react-dom@19
React 19 был в Next.js 15, так что обновлять его версию не нужно — достаточно обновить сам Next.js.
Шаг 2: Миграция конфигурации на TypeScript
Переименуйте next.config.mjs (или .js) в next.config.ts:
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
// ваша существующая конфигурация
};
export default nextConfig;
Если у вас были CommonJS-импорты (require), замените на ESM-импорты (import).
Шаг 3: Обновите блок turbopack
Если вы использовали experimental.turbo, перенесите его на верхний уровень:
// Было (Next.js 15)
const nextConfig: NextConfig = {
experimental: {
turbo: {
rules: { ... }
}
}
};
// Стало (Next.js 16)
const nextConfig: NextConfig = {
turbopack: {
rules: { ... }
}
};
Шаг 4: Проверьте совместимость Webpack-плагинов
Если в проекте были кастомные Webpack-конфигурации через webpack в next.config, проверьте их совместимость с
Turbopack. Большинство популярных плагинов уже поддерживаются:
@svgr/webpack — поддерживается через turbopack.rules
- CSS Modules — работают из коробки
- Tailwind CSS — работает из коробки
sass — поддерживается нативно
Если какой-то плагин не совместим, временно используйте --webpack флаг.
Шаг 5: Используйте after() вместо кастомных решений
Если вы использовали waitUntil() или ручные фоновые задачи для пост-обработки запросов, замените на after():
// Было
export async function POST(request: Request) {
const result = await processData(request);
// Хак с waitUntil или Promise без await
void sendAnalytics(result);
return Response.json(result);
}
// Стало
import { after } from "next/server";
export async function POST(request: Request) {
const result = await processData(request);
after(async () => {
await sendAnalytics(result);
});
return Response.json(result);
}
Шаг 6: Тестирование
После обновления проверьте:
- Сборка —
next build теперь использует Turbopack, убедитесь что проект собирается без ошибок
- Навигация — все переходы между страницами работают
- Server Actions — формы обрабатываются корректно
- Стили — CSS/Tailwind рендерятся правильно с Turbopack
- Middleware — проверьте совместимость с новой версией
Советы по оптимизации для production
Используйте Server Components по умолчанию
Все компоненты в Next.js 16 по умолчанию серверные. Добавляйте 'use client' только когда компоненту нужны клиентские
API: обработчики событий, useState, useEffect, браузерные API. Это минимизирует объём JavaScript на клиенте.
Стратегия кэширования
Составьте карту всех источников данных:
// Справочники — кэшируем надолго
const categories = await fetch(url, { next: { revalidate: 86400 } });
// Списки — кэшируем ненадолго
const products = await fetch(url, { next: { revalidate: 300 } });
// Персональные данные — без кэша
const cart = await fetch(url, { cache: "no-store" });
Мониторинг бандла
npm install -D @next/bundle-analyzer
// next.config.ts
import bundleAnalyzer from "@next/bundle-analyzer";
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === "true",
});
export default withBundleAnalyzer(nextConfig);
Статический экспорт для простых сайтов
Если проекту не нужен сервер (лендинги, блоги, корпоративные сайты), используйте статический экспорт:
const nextConfig: NextConfig = {
output: "export",
trailingSlash: true,
images: { unoptimized: true },
};
Это генерирует чистый HTML/CSS/JS, который можно раздавать через Nginx или любой CDN. Именно такой подход мы используем
для наших собственных проектов.
Заключение
Next.js 16 — зрелый релиз, который закрепляет всё лучшее из 15-й версии:
- Turbopack по умолчанию — ускоряет и dev, и build в 3-6 раз
- Нативный next.config.ts — типизированная конфигурация без обходных путей
- after() — стандартный способ выполнять код после ответа
- forbidden() / unauthorized() — чистая обработка 401/403 ошибок
- Стабильный PPR — статика + динамика на одной странице
- Улучшенное кэширование — предсказуемое и управляемое
Миграция с 15-й версии минимальна — главное проверить совместимость с Turbopack. Новые проекты рекомендуем начинать
сразу на Next.js 16.
Если вам нужна помощь с миграцией или разработкой нового проекта на Next.js, обращайтесь — у нас есть опыт
работы с крупными production-проектами на этом фреймворке.
Также рекомендуем ознакомиться с нашими материалами по SEO-продвижению, чтобы ваш Next.js-проект не только
быстро работал, но и хорошо индексировался поисковыми системами.