Выход Next.js 15 стал одним из самых ожидаемых событий во фронтенд-экосистеме. Vercel не просто добавили новые фичи — они фундаментально переработали подход к кэшированию, сделали Turbopack стабильным и открыли дверь к Partial Prerendering. В этой статье мы разберём каждое нововведение, оценим реальное влияние на production-проекты и дадим чёткий план миграции с Next.js 14.
Turbopack: наконец стабильный
Turbopack находился в бета-тестировании с момента анонса на Next.js Conf 2022. Три года доработок привели к тому, что в Next.js 15 он получил статус стабильного для dev-режима.
Что изменилось на практике
Turbopack заменяет Webpack в качестве бандлера для разработки. Ключевые улучшения:
- Холодный старт — до 76% быстрее по сравнению с Webpack
- Fast Refresh — обновление кода в браузере до 96% быстрее
- Инкрементальная компиляция — умное кэширование на уровне модулей
Для включения Turbopack достаточно добавить флаг:
next dev --turbopack
Или в package.json:
{
"scripts": {
"dev": "next dev --turbopack"
}
}
Реальные бенчмарки
Мы протестировали Turbopack на нескольких наших проектах разного масштаба:
| Метрика | Webpack (Next.js 14) | Turbopack (Next.js 15) | Разница |
|---|---|---|---|
| Холодный старт (малый проект) | 2.8 сек | 1.1 сек | -61% |
| Холодный старт (большой проект) | 12.4 сек | 3.2 сек | -74% |
| Fast Refresh | 320 мс | 45 мс | -86% |
| Потребление RAM | 1.2 ГБ | 0.8 ГБ | -33% |
Важное замечание: Turbopack для production-сборки всё ещё находится в разработке. Для next build по-прежнему используется Webpack. Vercel обещают стабильный production Turbopack в одном из следующих минорных релизов.
Революция в кэшировании
Пожалуй, самое важное и спорное изменение в Next.js 15 — полный пересмотр стратегии кэширования. В Next.js 14 агрессивное кэширование по умолчанию было источником множества багов и путаницы у разработчиков.
Что было раньше (Next.js 14)
В предыдущей версии:
fetch()кэшировался по умолчанию (нужно было явно указыватьcache: 'no-store')- Route Handlers с GET-запросами кэшировались по умолчанию
- Client Router Cache агрессивно кэшировал страницы
Это приводило к ситуациям, когда разработчики видели устаревшие данные и не могли понять почему.
Новый подход в Next.js 15
Теперь по умолчанию ничего не кэшируется:
// Next.js 14 — кэшируется по умолчанию
const data = await fetch('https://api.example.com/data');
// Next.js 15 — НЕ кэшируется по умолчанию
const data = await fetch('https://api.example.com/data');
// Next.js 15 — явное включение кэша
const data = await fetch('https://api.example.com/data', {
cache: 'force-cache'
});
Аналогично для Route Handlers:
// Next.js 15 — GET-запросы больше не кэшируются по умолчанию
export async function GET() {
const data = await getDataFromDB();
return Response.json(data);
}
// Для кэширования нужно явно указать
export const dynamic = 'force-static';
Изменения в Client Router Cache
Client Router Cache теперь не кэширует Page-компоненты по умолчанию. Параметр staleTimes позволяет настроить поведение:
// next.config.ts
const nextConfig = {
experimental: {
staleTimes: {
dynamic: 30, // секунды для динамических страниц
static: 180, // секунды для статических страниц
},
},
};
Это изменение ломает обратную совместимость, но делает поведение фреймворка предсказуемым. Больше никаких «магических» кэшей, которые невозможно отладить.
Server Actions: улучшения безопасности
Server Actions в Next.js 15 получили важные доработки в плане безопасности и удобства использования.
Автоматическое удаление неиспользуемых Actions
Ранее все Server Actions получали публичный HTTP-эндпоинт, даже если они не использовались на клиенте. В Next.js 15 неиспользуемые actions автоматически удаляются из клиентского бандла с помощью tree-shaking.
Улучшенная обработка ошибок
Новый паттерн для обработки ошибок в Server Actions:
'use server';
import { z } from 'zod';
const schema = z.object({
email: z.string().email(),
name: z.string().min(2),
});
export async function createUser(prevState: any, formData: FormData) {
const validatedFields = schema.safeParse({
email: formData.get('email'),
name: formData.get('name'),
});
if (!validatedFields.success) {
return {
errors: validatedFields.error.flatten().fieldErrors,
message: 'Ошибка валидации',
};
}
try {
await db.user.create({ data: validatedFields.data });
return { message: 'Пользователь создан', errors: null };
} catch (error) {
return { message: 'Ошибка при создании пользователя', errors: null };
}
}
Интеграция с useActionState
React 19 принёс хук useActionState (ранее useFormState), который теперь полностью интегрирован с Server Actions:
'use client';
import { useActionState } from 'react';
import { createUser } from './actions';
export function CreateUserForm() {
const [state, formAction, isPending] = useActionState(createUser, {
message: '',
errors: null,
});
return (
<form action={formAction}>
<input name="email" type="email" />
{state.errors?.email && <p>{state.errors.email}</p>}
<input name="name" type="text" />
{state.errors?.name && <p>{state.errors.name}</p>}
<button type="submit" disabled={isPending}>
{isPending ? 'Создание...' : 'Создать'}
</button>
{state.message && <p>{state.message}</p>}
</form>
);
}
Partial Prerendering (PPR)
Partial Prerendering — это экспериментальная, но крайне перспективная фича, которая позволяет комбинировать статический и динамический рендеринг на одной странице.
Как это работает
Представьте страницу интернет-магазина:
- Шапка, навигация, футер — статический контент, одинаковый для всех
- Корзина, персональные рекомендации — динамический контент, уникальный для каждого пользователя
С PPR Next.js отдаёт статическую оболочку мгновенно, а динамические части подгружаются стримингом:
import { Suspense } from 'react';
export default function ProductPage() {
return (
<main>
{/* Статическая часть — отдаётся мгновенно */}
<ProductInfo />
<ProductImages />
<ProductDescription />
{/* Динамическая часть — стримится отдельно */}
<Suspense fallback={<CartSkeleton />}>
<Cart />
</Suspense>
<Suspense fallback={<RecommendationsSkeleton />}>
<PersonalRecommendations />
</Suspense>
</main>
);
}
Для включения PPR необходимо добавить в конфигурацию:
// next.config.ts
const nextConfig = {
experimental: {
ppr: 'incremental',
},
};
И активировать для конкретного роута:
// app/product/[id]/page.tsx
export const experimental_ppr = true;
Зачем это нужно
PPR решает извечную дилемму: статика быстрая, но не персонализированная; динамика персонализированная, но медленная. С PPR вы получаете лучшее из двух миров — скорость статики и гибкость динамики.
React 19 под капотом
Next.js 15 полностью переходит на React 19, что приносит набор новых возможностей.
Новые хуки
useActionState— управление состоянием серверных действий (заменилuseFormState)useFormStatus— получение статуса отправки формы в дочерних компонентахuseOptimistic— оптимистичные обновления UI
Пример оптимистичного обновления:
'use client';
import { useOptimistic } from 'react';
import { addComment } from './actions';
export function Comments({ comments }: { comments: Comment[] }) {
const [optimisticComments, addOptimisticComment] = useOptimistic(
comments,
(state, newComment: string) => [
...state,
{ id: 'temp', text: newComment, pending: true },
]
);
async function handleSubmit(formData: FormData) {
const text = formData.get('text') as string;
addOptimisticComment(text);
await addComment(text);
}
return (
<div>
{optimisticComments.map((comment) => (
<div key={comment.id} style={{ opacity: comment.pending ? 0.5 : 1 }}>
{comment.text}
</div>
))}
<form action={handleSubmit}>
<input name="text" />
<button type="submit">Отправить</button>
</form>
</div>
);
}
Директива use client и use server
В React 19 директивы 'use client' и 'use server' стали официальной частью спецификации, а не фичей Next.js. Это важный шаг к стандартизации серверных компонентов в экосистеме React.
Руководство по миграции
Подготовка
Перед миграцией убедитесь, что ваш проект соответствует требованиям:
- Node.js — минимум версия 18.18.0
- React — обновление до React 19
- TypeScript — минимум версия 5.0
Пошаговая миграция
Шаг 1: Обновление зависимостей
npm install next@15 react@19 react-dom@19
npm install -D @types/react@19 @types/react-dom@19
Шаг 2: Запуск codemod
Next.js предоставляет автоматические codemod для миграции:
npx @next/codemod@latest upgrade latest
Этот инструмент автоматически исправит:
- Переименование
useFormStateвuseActionState - Обновление импортов
NextRequestдля Geo и IP - Замена
next/dynamicнаReact.lazyгде возможно
Шаг 3: Ручные исправления
После запуска codemod проверьте следующее:
- Все
fetch()вызовы — добавьтеcache: 'force-cache'где нужно кэширование - Route Handlers — добавьте
export const dynamic = 'force-static'для кэшируемых маршрутов - Проверьте работу форм с новым
useActionState
Шаг 4: Тестирование
# Запустите dev-сервер с Turbopack
next dev --turbopack
# Проверьте production-сборку
next build
next start
Breaking Changes: полный список
Критические изменения, на которые стоит обратить внимание:
fetch()не кэшируется по умолчанию — самое важное изменение, может повлиять на производительность если не добавить явное кэширование- Route Handlers не кэшируются по умолчанию — GET-эндпоинты нужно явно пометить как статические
NextRequest.geoиNextRequest.ipудалены — используйте middleware или платформенные решенияnext/dynamicизменено —suspenseprop удалён, используйтеSuspenseобёртку- Минимальная версия React — 19 — необходимо обновить все React-зависимости
Стоит ли обновляться?
Когда стоит обновляться сейчас
- У вас большой проект и вы страдаете от медленного dev-сервера — Turbopack даст ощутимое ускорение
- Вы боролись с непредсказуемым кэшированием в Next.js 14
- Вы хотите использовать React 19 и его новые хуки
- Вы начинаете новый проект — однозначно стартуйте на Next.js 15
Когда лучше подождать
- У вас стабильный production-проект без проблем — нет срочной необходимости
- Вы активно используете библиотеки, которые ещё не поддерживают React 19
- У вас сложная настройка Webpack с кастомными плагинами — Turbopack может не поддерживать все конфигурации
Наша рекомендация
Если у вас есть время на тестирование, обновляйтесь. Изменения в кэшировании делают фреймворк гораздо более предсказуемым, Turbopack реально ускоряет разработку, а React 19 приносит полезные паттерны. Но обязательно выделите время на проверку всех data-fetching сценариев — именно кэширование является главным источником потенциальных проблем при миграции.
Заключение
Next.js 15 — это зрелый и продуманный релиз. Vercel учли обратную связь от сообщества: убрали агрессивное кэширование, стабилизировали Turbopack, добавили экспериментальный PPR. Фреймворк движется в правильном направлении — к предсказуемому поведению и высокой производительности.
Если вам нужна помощь с миграцией на Next.js 15 или разработкой проекта на нём — команда QZX Studio готова помочь. Мы работаем с Next.js с первых версий и знаем все подводные камни.