Skip to content

Partial Prerendering (PPR) в продакшне: архитектурные паттерны (2026)

Partial Prerendering разрешает десятилетнюю дилемму между быстрой статической доставкой и серверной персонализацией. Страница декомпозируется на уровне Suspense-границ - CDN-обслуживаемые статические shells с TTFB < 50ms на маршрутах с динамическим, пользовательским и персонализированным контентом.

Архитектура Next.js Partial Prerendering - статический shell и динамические Suspense holes
Автор:Опубликовано:Обновлено:Время чтения:15 мин

Senior Frontend Architect, 10+ лет опыта построения production-проектов на Next.js. Contentful Certified Professional (2024). Специализация: React Server Components, headless eCommerce, инженерия Core Web Vitals.

Partial Prerendering (PPR) в Next.js, экспериментальный с версии 14 и постепенно доведённый до продакшена в 15, решает фундаментальный компромисс рендеринга, который сдерживал React-архитектуры с 2016 года: либо быстрый TTFB со статической генерацией и без серверной персонализации, либо корректная серверная персонализация с медленным TTFB. Подход PPR - собрать статическую HTML-оболочку на сборке и стримить динамику в границы Suspense на запросе - не подкрутка ни одной из моделей. Это другая модель рендеринга, соединяющая две вещи, которые раньше вместе не работали: конкурентный рендерер React со стримингом через Suspense и способность Next.js разделить один маршрут на статический артефакт сборки и потоковый рендерер времени запроса. В этом посте я разбираю, как устроен двухфазный ответ, как правильно расставлять границы Suspense, как PPR взаимодействует с Full Route Cache и какие цифры производительности это даёт в продакшене.

Часть I - Компромисс рендеринга, который решает PPR

Проще всего понять PPR на странице, где статика и динамика перемешаны. Возьмём карточку товара: название, описание и картинки статичны для SKU и кешируются на часы; цена зависит от географии и того, залогинен ли пользователь, и кешироваться не может; бейдж наличия обязан быть свежим; блок «Вы недавно смотрели» персональный. Каждая предыдущая стратегия рендеринга на такой странице ломается по-своему.

  • ISR (вся страница статична): Страница пререндерится на сборке с зафиксированными ценой и наличием. CDN отдаёт её с TTFB меньше 50 мс. Но цена может оказаться неверной для региона пользователя, а наличие устаревшим. Чтобы показать правильные значения, приложение вынуждено добавить клиентские запросы (useEffect → fetch → setState) и вернуть тот самый водопад, который RSC был призван убрать. Страница рисуется со скелетонами, пока выполняется JavaScript, и LCP деградирует.
  • SSR (вся страница динамична): Читает cookies() для определения региона, ходит в API цен, проверяет наличие - всё на запросе. Значения правильные. Но TTFB теперь 300–800 мс, то есть время разрешения всех зависимостей, вместо прежних 50. Этот TTFB напрямую задерживает LCP: браузер не может отрисовать самый крупный элемент, пока не пришёл первый байт.
  • Стриминговый SSR (без PPR): Конкурентный рендерер React умеет стримить куски HTML по мере готовности данных. Но без PPR весь маршрут остаётся динамическим: первый байт - это результат работы стримингового рендерера на запросе, а не закешированный ответ CDN. Оболочка вычисляется каждый раз заново. Стриминговый SSR улучшает TTFB относительно блокирующего SSR, отправляя оболочку до завершения запросов к базе, но сама оболочка всё равно генерируется на каждый запрос, а не отдаётся из кеша.
  • PPR (статическая оболочка плюс динамические дыры): Статическое содержимое товара - название, описание, картинки - пререндерится на сборке и кешируется на краю CDN. На запросе CDN мгновенно возвращает закешированную оболочку (TTFB меньше 50 мс), а затем открывает потоковое соединение с динамическим рендерером, который заполняет границы Suspense цены, наличия и персонализации по мере готовности данных. Элемент LCP - hero-картинка товара - лежит в статической оболочке и рисуется со скоростью CDN, а не со скоростью динамического рендеринга.

Часть II - Механизм двухфазного ответа

PPR разделяет рендеринг одного маршрута на две фазы, которые выполняются в разных вычислительных контекстах: фаза сборки производит статическую HTML-оболочку, фаза запроса стримит в неё динамику.

На сборке Next.js рендерит маршрут со всеми динамическими контекстами приостановленными: вызовы cookies(), headers() и обращения к searchParams внутри границ Suspense заставляют эти границы отрисовать свой фолбэк. Результат - целый HTML-документ, где динамические области представлены своими заглушками: скелетонами, индикаторами загрузки или пустыми плейсхолдерами. Этот HTML кладётся в краевой кеш CDN точно так же, как статически сгенерированная страница.

На запросе CDN мгновенно возвращает закешированную оболочку. HTTP-соединение остаётся открытым (chunked transfer encoding в HTTP/1.1 или мультиплексированные потоки HTTP/2 и HTTP/3), а запрос параллельно уходит динамическому рендереру - серверлес-функции на origin. Динамический рендерер выполняет обёрнутые в Suspense компоненты с настоящим контекстом запроса: cookies() читает реальные куки, headers() - реальные заголовки. По мере разрешения данных каждой границы рендерер стримит тег <script> с нагрузкой серверных компонентов, который велит рантайму React в браузере заменить заглушку настоящим содержимым. Это согласование DOM, а не полный перерендер, поэтому сдвига лейаута не будет, если у заглушки правильные размеры.

  • Механика chunked transfer encoding: Chunked transfer encoding в HTTP (RFC 7230) позволяет серверу отдавать ответ частями, не зная заранее общей длины. CDN отправляет статическую оболочку первым куском немедленно. Динамический рендерер дописывает следующие куски - теги <script> для каждой разрешившейся границы Suspense - в тот же HTTP-ответ по мере готовности. Браузер обрабатывает каждый кусок по приходу: первый - это полностью разбираемый HTML, который можно отрисовать и показать, не дожидаясь остальных.
  • Артефакт статической оболочки: Сгенерированная на сборке оболочка содержит целый HTML-документ: <html>, <head> со всеми метаданными и подсказками <link rel='preload'>, полное статическое дерево RSC и HTML заглушек для каждой динамической границы. Значит, браузер получает подсказку предзагрузки LCP-картинки уже в первом куске и начинает её тянуть, пока динамический рендерер ещё считает цену и наличие.
  • Изоляция динамического рендерера: Динамическая часть PPR-маршрута разворачивается как отдельный вызов серверлес-функции. У неё есть полный контекст запроса - куки, заголовки, параметры, - но выполняет она только поддеревья внутри Suspense. Архитектурно это не то же, что полный SSR-рендер: динамический рендерер делает меньше работы на запрос, что снижает и влияние холодного старта, и стоимость выполнения.

Часть III - Архитектура границ Suspense: правила статики и динамики

Граница Suspense - основная единица разбиения в PPR. Всё, что вне границы, статично; всё, что внутри, динамично. Архитектурная дисциплина здесь в том, чтобы ставить границы точно и оборачивать минимальную динамическую поверхность, а не максимальную.

  • Что делает рендер динамическим (обязано быть внутри Suspense, иначе PPR не работает): чтения cookies() и headers(), доступ к searchParams из пропсов страницы, вызовы Math.random(), вызовы new Date() без фиксированного значения, fetch() с cache: 'no-store' или next: { revalidate: 0 }, а также любой запрос данных, которому нужен контекст конкретного запроса: идентификатор пользователя из сессии, локаль из куки, вариант A/B-теста из куки.
  • Что безопасно статично (обязано быть СНАРУЖИ Suspense): название товара, описание, картинки и структура их URL, статическая навигация, футер, разметка schema.org, хлебные крошки (когда они не зависят от сессии), метаданные страницы, предзагрузка шрифтов и любые данные, одинаковые для всех пользователей и кешируемые с разумным TTL.
  • Антипаттерн размещения границ: Обернуть весь <main> в одну границу Suspense - значит убить PPR: всё видимое содержимое становится динамической дырой, а в статической оболочке остаются только <html>, <head>, <nav> и <footer>. Функционально это тот же полный SSR со скелетоном. Правильная гранулярность - одна граница на независимую динамическую задачу: цена, наличие, персональные рекомендации, состояние корзины. Независимые границы стримятся параллельно и разрешаются независимо: медленный запрос рекомендаций не задерживает отрисовку цены.
  • searchParams - самая частая ловушка PPR: В Next.js searchParams динамичен по определению: на сборке его знать нельзя. Компонент страницы, который деструктурирует searchParams из пропсов, переводит всю страницу в динамический рендер и обходит PPR целиком. Лечение - утащить работу с searchParams в дочерний компонент внутри Suspense: <Suspense fallback={<SortFallback />}><SortedProductList searchParams={searchParams} /></Suspense>. Статическая сетка товаров тогда придёт из CDN, а динамический запрос вызовет только отсортированный или отфильтрованный вариант.

Часть IV - Настройка PPR в Next.js 15

  • Инкрементальный режим (рекомендуется для продакшена): В next.config.ts: experimental: { ppr: 'incremental' }. Включение по маршрутам: export const experimental_ppr = true; сверху любого page.tsx или layout.tsx. Так PPR включается маршрут за маршрутом, а не глобально, и выкатывать его в прод можно безопасно, наблюдая за каждым шагом.
  • Глобальный режим: experimental: { ppr: true } включает PPR для всех маршрутов приложения. Это путь к полному переходу, но он требует, чтобы расстановка границ Suspense была проверена и подтверждена на каждом маршруте.
  • Совместимость с generateStaticParams: PPR работает с ним корректно. Статическая оболочка для каждого пререндеренного сочетания параметров лежит в кеше CDN. Динамические дыры разрешаются на каждый запрос с настоящим контекстом. ISR (revalidate = N) обновляет оболочку независимо от дыр: при перегенерации кешируется новое статическое содержимое, а динамика всегда считается заново.
  • Поведение при деплое на Vercel: На Vercel PPR-маршрут даёт два артефакта развёртывания: закешированный на CDN статический ассет (оболочка) и Edge- или серверлес-функцию (динамический рендерер). Ассет отдаётся глобально с краевых узлов, а рендерер выполняется в регионе, ближайшем к пользователю. Это разделение видно в аналитике Vercel: запросы статической оболочки не попадают в вызовы функций.

Часть V - Продакшен-сценарии, где PPR даёт максимум

Польза PPR пропорциональна доле статики в содержимом выше сгиба. Идеальный кандидат - страница, где элемент LCP и большинство видимого статичны, а осмысленная динамика (персонализация, состояние сессии, данные реального времени) живёт рядом или ниже.

  • Карточка товара (максимальный эффект): Статично: название, hero-картинка, описание, характеристики, хлебные крошки, разметка schema.org. Динамично и обёрнуто в Suspense: цена (зависит от географии), бейдж наличия, состояние кнопки «В корзину», рекомендации, сводка отзывов. Hero-картинка товара, обычно и есть элемент LCP на мобильных, лежит в статической оболочке и грузится из кеша CDN. Типичное улучшение LCP на карточке: с 1,8–2,4 с (SSR с походами в API) до 0,6–1,0 с. Как PPR встраивается в более широкую архитектуру headless-коммерции, разобрано в гайде по сборке headless Shopify.
  • Маркетинговые страницы с A/B-тестами: Статично: весь маркетинговый текст, hero-картинки, социальные доказательства. Динамично в Suspense: назначение варианта A/B (читается из куки эксперимента), персонализированный текст CTA. Без PPR любое A/B-тестирование через куки загоняет всю страницу в SSR. С PPR первый экран и текст приходят из CDN, а динамическими дырами остаются только чувствительные к эксперименту кнопки.
  • Дашборды за авторизацией: Статично: структура навигации, боковое меню, скелет страницы, неперсональные виджеты вроде приветствия по времени суток без имени. Динамично: имя пользователя, баланс счёта, уведомления, лента активности. Навигация, где на десктопе обычно и находится элемент LCP (логотип или баннер), грузится со скоростью CDN независимо от состояния авторизации.
  • Блоги и контентные сайты с читательскими функциями: Статично: сам материал - заголовок, текст, картинки, разметка, то есть подавляющее большинство страницы. Динамично: индикатор авторизации, статус закладки, вариант предложения подписки. Пост, где 95% страницы - это статья, оптимизируется под PPR практически бесплатно: статическая оболочка и есть почти вся страница.

Часть VI - Проектирование заглушек Suspense: нулевой CLS

Cumulative Layout Shift - метрика Core Web Vitals, которая при переходе на PPR рискует сильнее всего. Когда заглушку Suspense сменяет настоящее содержимое, а размеры у них разные, лейаут прыгает - и это событие CLS. Плохой CLS от PPR-заглушек хуже, чем от полного SSR-рендера, потому что пользователь видит прыжок уже после того, как страница отрисовалась.

  • Размеры скелетонов: У компонентов-заглушек должны быть явные фиксированные размеры, совпадающие с размерами настоящего содержимого. Заглушка цены вида <div className="h-7 w-20 animate-pulse bg-gray-200 rounded" /> подходит, если настоящая цена всегда занимает примерно 28 на 80 пикселей. Если цена может стать выше - например, в одну строку или в две с промо-бейджем, - заглушка обязана закладывать максимальную высоту, иначе содержимое уедет вниз.
  • min-height на динамических областях: Для динамики переменной высоты - карусели рекомендаций, сводки отзывов - задайте обёртке Suspense min-height под минимально ожидаемую высоту содержимого. Это не даёт странице сжаться в состоянии заглушки и растянуться, когда придут настоящие данные.
  • Loading.tsx как потоковая заглушка всей страницы: Файл loading.tsx в App Router создаёт границу Suspense вокруг всего сегмента страницы. Он уместен для переходов между маршрутами, но не должен быть единственной границей в PPR-маршруте: тогда вся страница станет динамической. Используйте loading.tsx для состояний навигации, а для разбиения на PPR-границы - мелкие встроенные Suspense.
  • Подсказки предзагрузки в статической оболочке: Поскольку оболочка приходит первым HTML, любые теги <link rel='preload'> в её <head> срабатывают немедленно. Для LCP-картинки это означает <link rel='preload' as='image' href='/product-hero.avif' /> в <head>, который генерирует компонент next/image: загрузка изображения стартует, пока браузер ещё принимает статический HTML. Сочетание быстрого TTFB от CDN и ранней предзагрузки картинки и даёт то улучшение LCP, ради которого PPR затевался.

Часть VII - PPR и Full Route Cache

Чтобы предсказывать выигрыш в TTFB на проде, надо понимать, как PPR взаимодействует с Full Route Cache. Этот кеш хранит отрисованный вывод маршрута, чтобы не рендерить на каждый запрос. Без PPR любой маршрут, который читает cookies() или headers(), из кеша выпадает и обязан рендериться на сервере каждый раз. Именно так любая персонализация или проверка авторизации загоняет страницу в SSR.

  • PPR возвращает Full Route Cache персонализированным маршрутам: Когда cookies() вызывается внутри границы Suspense, то есть в динамической дыре PPR, Next.js знает, что динамично только это поддерево, а остальной маршрут кешируем. Статическая оболочка попадает в Full Route Cache и в кеш CDN независимо от того, что происходит внутри границ. Вызов cookies() больше не отравляет кешируемость всего маршрута.
  • Инвалидация оболочки и динамики независима: Оболочка инвалидируется ревалидацией ISR - по времени или по требованию через revalidateTag. Динамика не кешируется вообще и считается заново на каждый запрос. Страница товара с revalidate = 3600 перегенерирует оболочку раз в час, а цена и наличие в дырах Suspense всегда отражают текущее состояние. Такое разделение статической свежести (раз в час) и динамической (на каждый запрос) архитектурно чище любых альтернатив.
  • Край CDN против выполнения на origin: Статическая оболочка отдаётся прямо с краевого узла CDN по всему миру, TTFB примерно 10–50 мс в зависимости от географической близости. Динамические дыры выполняются на origin-сервере или в Edge-функции Vercel для глобального распределения. Воспринимаемый пользователем TTFB - это время ответа CDN: задержка динамического рендеринга спрятана за потоковым соединением, которое уже открылось, когда CDN отдал первый байт.

Часть VIII - Измеренные результаты

Выигрыш от PPR лежит прежде всего в TTFB и LCP. Профиль ниже репрезентативен для продакшен-развёртываний на Vercel с получением данных через Storefront API Shopify (задержка API до origin 50–200 мс).

  • TTFB: Полный SSR с походами в API: 300–800 мс (p75). PPR с закешированной оболочкой: 20–80 мс (p75). Улучшение в 4–10 раз. Оно не зависит от задержки вышестоящего API, потому что оболочка возвращается до того, как сделан хоть один динамический вызов.
  • LCP: На карточке, где hero-картинка лежит в статической оболочке, полный SSR даёт LCP 1,8–2,4 с на мобильных p75, а PPR - 0,6–1,2 с. Сокращение складывается из двух эффектов: TTFB ниже, то есть HTML приходит раньше, и <link rel='preload'> срабатывает раньше, то есть загрузка картинки стартует с первым байтом HTML. Механика LCP и методика измерения разобраны в архитектуре веб-производительности.
  • INP: Прямого влияния на INP у PPR нет. INP меряет отзывчивость взаимодействий, то есть задержку обработчика, а не загрузку страницы. Косвенная польза есть: меньшие клиентские бандлы (RSC рендерит больше на сервере) сокращают время разбора JS, а это может снизить INP на взаимодействиях во время загрузки. Но это заслуга RSC, а не самого PPR. Методика измерения и полный план снижения - в оптимизации INP в Next.js 16.
  • CLS: Итог целиком зависит от того, как спроектированы заглушки. Хорошие скелетоны с явными размерами дают CLS 0, то есть сдвига нет. Плохие - например, display: none, который сменяется блоком в 200 пикселей, - дают CLS больше 0,1. PPR добавляет риск CLS, которого при полном SSR нет: за это приходится платить осознанным проектированием заглушек.

Часть IX - Фреймворк решения: PPR или альтернативы

  • Берите PPR, когда: (1) На странице есть ясная статическая область выше сгиба, и в ней лежит элемент LCP. (2) Динамика ограничена конкретными узнаваемыми областями - цена, состояние сессии, рекомендации, - которые можно изолировать в границы Suspense. (3) Текущая архитектура - полный SSR с TTFB выше 300 мс из-за вышестоящих зависимостей. (4) Вы на Vercel или другой платформе, которая поддерживает связку статической оболочки и стриминга. (5) Core Web Vitals показывают LCP выше 2,5 с на мобильных p75, и главный вклад вносит именно TTFB.
  • Берите ISR плюс клиентский запрос (без PPR), когда: (1) Динамика лежит ниже сгиба и на LCP не влияет. (2) Динамика редкая или необязательная, вроде бейджа уведомлений, и клиентский водопад приемлем. (3) Команда ещё не освоила дисциплину границ Suspense, и риск получить CLS из-за ошибок в заглушках высок.
  • Берите полный SSR (без PPR), когда: (1) Всё выше сгиба динамично и персонально, статической оболочки просто нет. (2) Странице нужны цены аукциона или ставок в реальном времени, синхронные по всему HTML-ответу. (3) Приложение развёрнуто на платформе, которая связку CDN и стриминга не поддерживает.
  • PPR - неправильный выбор, когда: searchParams определяет весь лейаут страницы, например на странице результатов поиска, где позиция, сортировка и фильтры влияют на всё выше сгиба. Тогда статическая оболочка выродится в скелетон без осмысленного содержимого, и выигрыш PPR исчезнет. Для страниц поиска обычно лучше ISR с клиентской фильтрацией или полный SSR с агрессивным краевым кешированием.

Часть X - Известные ограничения и продакшен-оговорки

  • Распространение searchParams: Как сказано в третьей части, любой компонент в дереве статической оболочки, который трогает searchParams, переводит всю страницу в динамический рендер. Это типичная боль миграции: перед включением PPR на маршруте прогоните поиск по использованию searchParams в дереве компонентов и уведите все обращения внутрь Suspense.
  • Middleware и правка заголовков ответа: Middleware, который меняет заголовки ответа - например, ставит Set-Cookie или Vary в зависимости от содержимого запроса, - может неожиданно взаимодействовать с закешированной статической оболочкой: она закеширована без знания об этих правках. Проверяйте связку middleware и PPR явно до выката в прод.
  • Холодный старт динамического рендерера: На серверлес-платформах у функции динамического рендерера есть штраф холодного старта, 50–500 мс на первом вызове после простоя. Это влияет на задержку разрешения динамических дыр, но не на TTFB статической оболочки. У маршрутов с постоянным трафиком холодные старты редки; у малопосещаемых они способны сделать динамические дыры медленнее ожидаемого.
  • PPR в Next.js 15 формально experimental (инкрементальный режим стабилен для прода): Флаг experimental_ppr = true на маршруте документирован как готовый к продакшену в инкрементальном режиме. Глобальная настройка ppr: true всё ещё считается экспериментальной. Следите за changelog Next.js: перевод в стабильные ожидается в Next.js 16.
  • Границы ошибок в динамических дырах: Если запрос данных в динамической дыре падает - таймаут API, ответ 500, - граница Suspense откатывается к своему Error Boundary из error.tsx. Статическая оболочка при этом остаётся целой и отрисованной. Пользователь видит страницу с деградацией: статика на месте, а в динамических дырах состояние ошибки, - вместо полностраничной ошибки. Архитектурно это лучше SSR, где один упавший вышестоящий вызов даёт 500 на всю страницу.

Заключение

PPR - самое крупное изменение архитектуры рендеринга в Next.js со времён RSC. Он чинит структурную проблему, которую ISR, SSR и стриминговый SSR оставляли открытой: нельзя было отдавать закешированный на CDN HTML с TTFB меньше 50 мс на маршруте, где есть серверная динамика. Подход через границы Suspense - оболочка на сборке, заполнение стримингом на запросе - чисто ложится на остальную модель App Router.

На практике он требует дисциплины границ: у каждой динамической задачи должна быть своя граница Suspense, а у каждой заглушки - правильные размеры, чтобы не породить CLS. Команды, которые вкладываются в эту инженерию границ, видят улучшение LCP в полевых данных CrUX уже через несколько дней после выката. Как PPR встраивается в модель рендеринга App Router целиком, разобрано в гайде по миграции на App Router.

Ревью архитектуры PPR, внедрение или инженерия Core Web Vitals - услуга оптимизации производительности | кейсы | обсудить проект.

Частые вопросы

  • Доступен ли PPR в Next.js 14? PPR появился в Next.js 14 как экспериментальный под флагом experimental: { ppr: true }. Инкрементальный режим ('incremental'), который позволяет включать его по маршрутам, добавили в Next.js 15. Для продакшена рекомендуемый путь - инкрементальный режим в 15-й версии.
  • Работает ли PPR с getServerSideProps? Нет. PPR - возможность только App Router, а getServerSideProps относится к Pages Router. Если вы на Pages Router и вам нужны возможности PPR, сначала переезжайте на App Router - смотрите гайд по миграции.
  • Можно ли использовать PPR с мультиязычностью? Да. Статическая оболочка генерируется для каждой локали, если у вас маршруты с локалью в пути. app/[locale]/product/[handle]/page.tsx даёт по оболочке на каждое сочетание локали и хендла товара. Динамические дыры - цена, состояние сессии - разрешаются на каждый запрос независимо от локали.
  • Как PPR влияет на Time to Interactive? Косвенно улучшает: меньшие клиентские бандлы (у статики, отрисованной через RSC, клиентского JS нет) сокращают время разбора. Основной вклад PPR - в TTFB и LCP, а не напрямую в TTI. Если у вас высокий TTI, корень проблемы скорее в избытке клиентского JavaScript - смотрите разбор границ 'use client' в гайде по миграции на App Router.
  • Что будет, если динамический рендерер медленный? Пользователь видит заглушки Suspense всё время, пока идёт динамический рендер. Содержимое статической оболочки при этом полностью видно и работает: навигация кликается, статический текст читается. Это принципиально лучше полного SSR, где вся страница ждёт самую медленную вышестоящую зависимость.

Источники

Похожие статьи

Shopify Hydrogen vs Next.js Commerce: какую архитектуру выбрать для магазина в 2026

Детальное сравнение двух основных headless-архитектур для Shopify. Модель исполнения, слой данных, стратегия кэширования, размер бандла, бенчмарки производительности (LCP/INP/CLS), TCO, риск вендор-локина и практический фреймворк решений между нативной глубиной Hydrogen и компонуемой гибкостью Next.js Commerce.

eCommerceNext.jsShopify
Читать статью

Архитектура веб-производительности: системный анализ 12 инженерных принципов (издание 2026)

Скачок Lighthouse на 25 пунктов - с 72 до 99 на десктопе - редко складывается из сотни микроправок; он берётся из переноса двух-трёх ассетов с основного потока. Глубокий технический разбор веб-производительности 2026 года: физика сетей, конвейеры изображений, модели выполнения JavaScript, Critical Rendering Path, edge-вычисления, Core Web Vitals и продакшн RUM - с реальным продакшн-кейсом и конкретными примерами реализации.

EngineeringArchitectureCore Web Vitals
Читать статью

React 19: useOptimistic, use(), Server Actions - механизм и архитектура (2026)

Детальный разбор пяти новых примитивов React 19: useOptimistic (конкурентная composition оптимистичного состояния с автоматическим rollback), use() (условное чтение Promise и Context), Server Actions (RPC-over-HTTP контракт и Progressive Enhancement), useActionState (стейт-threading паттерн), useFormStatus. Action-based mutation архитектура, которая заменяет Redux/Zustand для серверных мутаций.

React 19ArchitectureNext.js
Читать статью

Похожие услуги

Эти материалы напрямую связаны с услугами, которые я реализую в продакшене. Изучите услугу или обсудите свой случай.