<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Next.js 15: Partial Prerendering с React 19 useActionState для динамических форм]]></title><description><![CDATA[<p dir="auto"><img src="/assets/uploads/files/79/67/19/1773909384767-generated_1773909044171-resized.webp" alt="Обложка: Next.js 15: Partial Prerendering с React 19 useActionState для динамических форм с мгновенной валидацией" class=" img-fluid img-markdown" /></p>
<p dir="auto">Зацените, как в Next.js 15 Partial Prerendering (PPR) сочетается с useActionState из React 19 для динамических форм. Это киллер-фича для мгновенной валидации без полной перезагрузки страницы. Теперь статическая оболочка страницы летает, а формы обновляются на лету - DX на новом уровне.</p>
<p dir="auto">PPR решает боль: раньше один динамический кусок кидал всю страницу в SSR, убивая производительность. С флагами experimental_ppr и Suspense вы гранулярно контролируете, что статично, а что стримится. А useActionState добавляет реактивный стейт для форм с серверными экшнами - валидация мгновенная, без лишних запросов. Идеально для дашбордов, корзин, профилей.</p>
<h2>Под капотом PPR: статическая оболочка + динамические дыры</h2>
<p dir="auto">PPR в Next.js 15 делит рендер на статическую shell и динамические холы через Suspense. На билде генерится статический HTML с fallback’ами, а при запросе стримятся только нужные части - cookies, headers или query не ломают весь маршрут. Это как ISR + SSR в одном флаконе, но умнее: граница Suspense определяет, где динамика.</p>
<p dir="auto">На первый взгляд кажется круто, но есть нюанс - пока экспериментально, не для продакшена на больших проектах. Подключается в next.config.js через experimental.ppr: ‘incremental’, плюс export const experimental_ppr = true в page.tsx. Next.js автоматически prerender’ит shell, а fallback’и (скелетоны, лоадеры) заменяются динамикой в том же HTTP-запросе. Web Vitals взлетают: LCP быстрее, FID нулевой.</p>
<p dir="auto">Вот базовый сетап:</p>
<ul>
<li><strong>next.config.js</strong>: добавьте <code>experimental: { ppr: 'incremental' }</code> - включает инкрементальный PPR.</li>
<li><strong>page.tsx</strong>: <code>export const experimental_ppr = true</code> - активирует для маршрута и детей.</li>
<li><strong>Suspense wrapper</strong>: оберните динамику, типа <code>&lt;Suspense fallback={&lt;Skeleton /&gt;}&gt;&lt;DynamicForm /&gt;&lt;/Suspense&gt;</code> - shell статичен, дыра стримится.</li>
<li><strong>Динамические триггеры</strong>: cookies(), searchParams() внутри Suspense - не крашат статическую часть.</li>
</ul>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Сравнение рендера</th>
<th>Без PPR</th>
<th>С PPR</th>
</tr>
</thead>
<tbody>
<tr>
<td>Статическая часть</td>
<td>Нет</td>
<td>Shell на билде</td>
</tr>
<tr>
<td>Динамика</td>
<td>Вся страница SSR</td>
<td>Только в Suspense</td>
</tr>
<tr>
<td>TTFB</td>
<td>200-500ms</td>
<td>&lt;100ms</td>
</tr>
<tr>
<td>ISR совместимость</td>
<td>Частично</td>
<td>Полная</td>
</tr>
</tbody>
</table>
<h2>useActionState в React 19: реактивные формы без костылей</h2>
<p dir="auto">useActionState - новый хук React 19 для серверных экшенов в формах. Он возвращает [state, action, isPending] - стейт обновляется автоматически после submit, без useState + useTransition. Идеально с PPR: форма в динамической дыре, валидация серверная, UI реактивный. Никакого бойлерплейта с ручным стейтом или эффектами.</p>
<p dir="auto">Под капотом action - async функция, которая мутирует данные на сервере (DB, API), возвращает ошибку или успех. Хук трекает pending, обновляет state оптимистично. В Next.js 15 это рендерится в RSC (React Server Components), стримится в PPR-дыру. Мгновенная валидация: ошибки показываются сразу, без фулл-рилоада. Круто для сложных форм - рег, логин, корзина.</p>
<p dir="auto">Короткий пример на TypeScript:</p>
<pre><code class="language-tsx">import { useActionState } from 'react';

async function loginAction(prevState: any, formData: FormData) {
  const email = formData.get('email');
  // серверная валидация
  if (!email) return { error: 'Email обязателен' };
  // API call
  return { success: true };
}

export default function LoginForm() {
  const [state, submitAction, pending] = useActionState(loginAction, null);
  return (
    &lt;form action={submitAction}&gt;
      &lt;input name="email" /&gt;
      {state?.error &amp;&amp; &lt;p&gt;{state.error}&lt;/p&gt;}
      &lt;button disabled={pending}&gt;Войти&lt;/button&gt;
    &lt;/form&gt;
  );
}
</code></pre>
<ul>
<li><strong>prevState</strong>: кумулятивные ошибки, не теряются при повторном сабмите.</li>
<li><strong>isPending</strong>: блокирует UI, показывает спиннер - UX на уровне.</li>
<li><strong>Серверный рендер</strong>: action выполняется на сервере, стейт стримится клиенту.</li>
<li><strong>TypeScript</strong>: типизуйте state как <code>LoginState | null</code> для автокомплита.</li>
</ul>
<h2>Комбо PPR + useActionState: динамические формы в продакшен-стиле</h2>
<p dir="auto">Оборачиваем форму в Suspense внутри PPR-страницы: статический хедер/нейв, динамическая форма стримится. useActionState внутри - submit бьет сервер, state обновляется в дыре, без гидрации всего. Валидация мгновенная: поле подсвечивается, ошибки inline. Оптимизация Web Vitals: CLS минимальный, благодаря скелетонам.</p>
<p dir="auto">Это просто боль решается: раньше формы лагали из-за полного SSR. Теперь гранулярно - шапка кэшируется, форма реактивна. Нюанс: в client-nav пока не стримит (soon), но для initial load огонь. Пример - дашборд с поиском: query в searchParams триггерит динамику, но только в таблице.</p>
<p dir="auto">Полный пример страницы:</p>
<pre><code class="language-tsx">// app/dashboard/page.tsx
export const experimental_ppr = true;

import { Suspense } from 'react';
import LoginForm from './LoginForm';

export default function Dashboard() {
  return (
    &lt;div&gt;
      &lt;h1&gt;Дашборд&lt;/h1&gt; {/* статично */}
      &lt;Suspense fallback={&lt;div&gt;Загрузка формы...&lt;/div&gt;}&gt;
        &lt;LoginForm /&gt;
      &lt;/Suspense&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Преимущества комбо</th>
<th>Описание</th>
</tr>
</thead>
<tbody>
<tr>
<td>Мгновенная валидация</td>
<td>State обновляется в реал-тайм</td>
</tr>
<tr>
<td>Core Web Vitals</td>
<td>Shell + стриминг = топ метрики</td>
</tr>
<tr>
<td>DX</td>
<td>Меньше хуков, чистый код</td>
</tr>
<tr>
<td>Масштаб</td>
<td>Работает с ISR, не ломается</td>
</tr>
</tbody>
</table>
<h2>Фичи, которые меняют правила игры</h2>
<p dir="auto">PPR с useActionState - это шаг к default-рендеру будущего: стат по умолчанию, динамика по необходимости. Осталось доработать client-side стриминг и full-prod readiness, но уже сейчас оптимизирует бандл и TTI. Подумать стоит над миграцией legacy-SSR чанков в Suspense - там пушка по перформансу. А вы уже тестите в пати-проектах?</p>
]]></description><link>https://forum.exlends.ru/topic/1752/next.js-15-partial-prerendering-s-react-19-useactionstate-dlya-dinamicheskih-form</link><generator>RSS for Node</generator><lastBuildDate>Sat, 17 Oct 2026 03:52:47 GMT</lastBuildDate><atom:link href="https://forum.exlends.ru/topic/1752.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 19 Mar 2026 08:36:25 GMT</pubDate><ttl>60</ttl></channel></rss>