С вами Дима, старший фронтенд разработчик в Surf, и сегодня мы разберём React 19 — новую версию одной из самых популярных библиотек для создания пользовательских интерфейсов.

Команда React подготовила много интересного — обновление обещает упростить разработку, повысить производительность и открыть новые горизонты для создания современных приложений.
Так что вперёд, к подробному разбору основных фичей, оценке их плюсов и минусов и исследованию будущего React.
Что нового в React 19
React 19 — это не простой апгрейд, это полноценный шаг вперёд. В обновлении появились инструменты для автоматической оптимизации, новые подходы к серверному рендерингу и улучшения для повседневной работы.
1. React Compiler: турбоускоритель кода
React Compiler — это, пожалуй, самая громкая новинка версии. Представьте инструмент, который берёт React-код и автоматически превращает его в более быстрый JavaScript — а ещё избавляет от рутинной оптимизации. Компилятор может ускорить рендеринг до двух раз, устраняя необходимость вручную использовать useMemo, useCallback или оборачивать компоненты в memo.
Как это работает
Компилятор анализирует код на этапе сборки и определяет, какие части компонентов от каких данных зависят.
Затем он автоматически добавляет оптимизации: если данные не изменились, React не будет перерисовывать компонент. Это похоже на подход, который используют Svelte или Solid.js, где оптимизация происходит на этапе компиляции, но в React это сделано более гибко — компилятор пока опционален и не ломает существующий код.
Пример кода
Допустим, есть компонент, который выполняет сложные вычисления для рендера списка. В React 18 нам пришлось бы оптимизировать его вручную:
// React 18
import { memo, useMemo } from 'react';
const List = memo(({ items }) => {
const computedItems = useMemo(() => {
return items.map((item) => ({
...item,
value: expensiveComputation(item),
}));
}, [items]);
return (
<ul>
{computedItems.map((item) => (
<li key={item.id}>{item.value}</li>
))}
</ul>
);
});
function expensiveComputation(item) {
// Имитация сложных вычислений
return item.name.toUpperCase() + Math.random();
}
В React 19 с компилятором тот же код можно написать проще:
// React 19
function List({ items }) {
const computedItems = items.map((item) => ({
...item,
value: expensiveComputation(item),
}));
return (
<ul>
{computedItems.map((item) => (
<li key={item.id}>{item.value}</li>
))}
</ul>
);
}
function expensiveComputation(item) {
return item.name.toUpperCase() + Math.random();
}
Компилятор сам поймёт, что нужно кешировать, и предотвратит лишние рендеры. Это полезно в больших проектах, где ручная оптимизация занимает много времени.
Статус
К марту 2025 года React Compiler остаётся экспериментальной фичей, при этом он активно тестируется. Например, команда Instagram* использует его в продакшене, сообщая о заметном приросте производительности. В будущем компилятор станет частью стандартного рабочего процесса React, а его код откроют для сообщества.
Плюсы
Автоматическая оптимизация без лишнего кода.
Ускорение работы приложений даже без глубоких знаний о мемоизации.
Меньше багов из-за упущенных зависимостей в useMemo или useCallback.
Минусы
Экспериментальный статус требует тщательного тестирования перед использованием в продакшене.
Возможны конфликты с необычными паттернами кода, которые компилятор пока не поддерживает.
Требуется время на интеграцию в существующие проекты.
2. Server Components: сила сервера в ваших руках
Серверные компоненты (Server Components) — это новый подход к рендерингу, который позволяет частично перенести логику на сервер. Теперь можно пометить компонент директивой "use server", и он будет исполняться только на сервере, отправляя клиенту готовый HTML. Это ускорит загрузку страниц, улучшит SEO и снизит нагрузку на клиент.
Как это работает
Серверные компоненты рендерятся на сервере, а после их результат передаётся клиенту. При этом они могут взаимодействовать с клиентскими компонентами (помеченными как "use client"), создавая гибридную архитектуру. Например, тяжёлые операции вроде запросов к базе данных можно выполнять на сервере, а интерактивные элементы — оставить на клиенте.
Пример серверного компонента
// server-component.js
'use server';
export async function fetchUserData() {
const res = await fetch('https://api.example.com/user');
return res.json();
}
// page.js
import { fetchUserData } from './server-component';
export default async function UserProfile() {
const user = await fetchUserData();
return (
<div>
<h1>{user.name}</h1>
<p>{user.bio}</p>
</div>
);
}
Здесь fetchUserData выполняется на сервере, а клиент получает готовый HTML с данными пользователя. Если добавить клиентский компонент, например, кнопку, React сам разберётся, как соединить серверную и клиентскую части.
Реальный кейс
Допустим, мы строим блог. Серверные компоненты могут рендерить посты из базы данных, а клиентский компонент — форму комментариев. Это снижает объём JavaScript, отправляемого клиенту, и ускоряет первую загрузку.
Плюсы
Ускорение первой загрузка страницы благодаря серверному рендерингу.
Улучшение SEO — поисковики сразу видят контент.
Возможность выполнять ресурсоёмкие задачи — тот же парсинг данных — на сервере.
Минусы
Нужна поддержка фреймворка (например, Next.js), потому что React сам по себе не управляет сервером.
Не все компоненты можно сделать серверными — те, что используют window или другие браузерные API, останутся клиентскими.
Усложняет архитектуру, если мы не привыкли к разделению сервер-клиент.
3. Actions: прощайте сложности с формами
Actions — герой работы с формами и асинхронными мутациями в React 19. Это новый инструмент для управления отправкой данных, который автоматически обрабатывает состояния загрузки, ошибки и даже оптимистичные обновления интерфейса.
Как это работает
Вы определяете функцию Action и передаёте её в пропс action тега <form>. React берёт на себя управление состоянием: показывает, что форма отправляется, обрабатывает ошибки и позволяет обновлять UI до завершения операции.
Пример
import { useTransition } from 'react';
function CommentForm() {
const [isPending, startTransition] = useTransition();
async function handleSubmit(formData) {
startTransition(async () => {
const response = await fetch('/api/comments', {
method: 'POST',
body: formData,
});
if (!response.ok) throw new Error('Ошибка отправки');
});
}
return (
<form action={handleSubmit}>
<input name="comment" placeholder="Ваш комментарий" />
<button type="submit" disabled={isPending}>
{isPending ? 'Отправка...' : 'Отправить'}
</button>
</form>
);
}
Здесь useTransition помогает React управлять асинхронной операцией, а isPending показывает состояние загрузки. Можно добавить обработку ошибок или оптимистичное обновление с помощью хука useOptimistic.
Расширенный пример с ошибками
import { useTransition, useState } from 'react';
function CommentForm() {
const [isPending, startTransition] = useTransition();
const [error, setError] = useState(null);
async function handleSubmit(formData) {
startTransition(async () => {
try {
const response = await fetch('/api/comments', {
method: 'POST',
body: formData,
});
if (!response.ok) throw new Error('Ошибка сервера');
} catch (err) {
setError(err.message);
}
});
}
return (
<form action={handleSubmit}>
<input name="comment" placeholder="Ваш комментарий" />
<button type="submit" disabled={isPending}>
{isPending ? 'Отправка...' : 'Отправить'}
</button>
{error && <p style={{ color: 'red' }}>{error}</p>}
</form>
);
}
Плюсы
Упрощает работу с формами, избавляет от лишнего состояния.
Встроенная поддержка загрузки и ошибок.
Легко интегрируется с оптимистичными обновлениями через useOptimistic.
Минусы
Требует переосмысления привычных подходов к формам (например, если вы использовали onSubmit).
Может быть сложным для новичков, особенно при комбинировании с другими хуками.
Пока не все кейсы покрыты из коробки — например, сложные валидации не тронуты.
4. Новый API use: упрощаем работу с ресурсами
API use — это способ работать с асинхронными ресурсами (промисами, контекстом) прямо в рендере. В отличие от хуков, его можно вызывать условно, что даёт больше свободы.
Как это работает?
use принимает ресурс, например, промис, и возвращает его значение, когда оно готово. React приостанавливает рендеринг компонента, пока данные не загрузятся, и использует Suspense для показа fallback.
Пример
import { Suspense } from 'react';
function UserData({ userPromise }) {
const user = use(userPromise);
return <h1>{user.name}</h1>;
}
function App() {
const promise = fetch('/api/user').then((res) => res.json());
return (
<Suspense fallback={<p>Загрузка...</p>}>
<UserData userPromise={promise} />
</Suspense>
);
}
Если промис ещё не разрешён, пользователь увидит «Загрузка...», а затем — имя пользователя.
Условный вызов
function Component({ condition, promise }) {
if (condition) {
const data = use(promise);
return <div>{data}</div>;
}
return <div>Ничего нет</div>;
}
Отметим, что это невозможно сделать с обычными хуками вроде useEffect, где вызовы должны быть строго на верхнем уровне.
Плюсы
Упрощает работу с асинхронными данными без лишних хуков.
Поддержка условных вызовов делает код более гибким.
Идеально сочетается с Suspense и серверными компонентами.
Минусы
Неочевидное поведение для тех, кто привык к строгим правилам хуков.
Требует осторожности, чтобы не вызвать бесконечные циклы рендеринга.
Ограниченная документация на ранних стадиях.
5. Улучшения в работе с рефами и хуками
React 19 упрощает работу с рефами и добавляет новые хуки для улучшения UX. Теперь рефы для функциональных компонентов передаются как обычные пропсы, и не нужно использовать forwardRef. Но главные звёзды этого обновления — новые хуки useFormStatus и useOptimistic. Они решают ряд задач разработчиков и делают взаимодействие с пользователем более плавным.
Пример рефов
// React 18
import { forwardRef } from 'react';
const MyInput = forwardRef((props, ref) => <input ref={ref} {...props} />);
// React 19
function MyInput(props) {
return <input {...props} />;
}
// Использование
function Parent() {
const inputRef = useRef(null);
return <MyInput ref={inputRef} />;
}
Новые хуки
useFormStatus
Этот хук даёт информацию о состоянии формы, в которой он используется. Он полезен, когда мы работаем с Actions и хотим отслеживать, отправляется ли форма в данный момент, или получать дополнительные данные о её статусе.
Хук возвращает объект с полями pending (булево значение, показывающее, выполняется ли отправка), data (объект FormData с данными формы) и method (метод формы, например, "POST" или "GET").
Важно: useFormStatus работает только внутри компонента, который рендерится в контексте формы с пропсом action.
Пример с useFormStatus
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending, data } = useFormStatus();
// Можно использовать data для дополнительных проверок
const comment = data?.get('comment') || '';
return (
<button type="submit" disabled={pending}>
{pending ? `Отправка "${comment}"...` : 'Отправить комментарий'}
</button>
);
}
function CommentForm() {
async function handleSubmit(formData) {
await fetch('/api/comments', {
method: 'POST',
body: formData,
});
}
return (
<form action={handleSubmit}>
<input name="comment" placeholder="Ваш комментарий" />
<SubmitButton />
</form>
);
}
Кнопка «Отправить» становится неактивной во время отправки, а текст кнопки динамически обновляется, показывая, что именно отправляется. Это удобно для улучшения UX, особенно в формах с длительными операциями, например, загрузкой файлов.
Хук избавляет нас от необходимости вручную передавать состояние загрузки через пропсы или контекст — React сам отслеживает статус формы.
Когда использовать
Если нужно показать пользователю, что форма обрабатывается.
Когда требуется доступ к данным формы внутри дочерних компонентов без их явной передачи.
Для создания динамических интерфейсов, зависящих от статуса отправки.
Плюсы
Упрощает отслеживание состояния формы без лишнего кода.
Доступ к data позволяет создавать динамичные элементы UI, зависящие от введённых данных.
Улучшает UX за счёт встроенной обработки загрузки.
Минусы
Работает только внутри формы с action, что ограничивает его гибкость.
Не решает задачи сложной валидации или обработки ошибок.
Может быть избыточным для простых форм без асинхронных операций.
useOptimistic
Этот хук позволяет реализовать так называемое оптимистичное обновление UI. Что это значит? Оптимистичное обновление — это подход, при котором мы сразу показываем пользователю результат операции — добавление комментария или лайк — не дожидаясь ответа от сервера. Если операция завершится успешно, UI будет в актуальном состоянии. Если произойдёт ошибка, мы откатим изменения. Такой подход делает интерфейс более отзывчивым и улучшает восприятие скорости приложения.
Хук useOptimistic принимает текущее состояние и коллбэк для его «оптимистичного» обновления, возвращая массив с оптимистичным состоянием и функцией для его изменения.
Пример использования useOptimistic
import { useOptimistic } from 'react';
function CommentList({ comments, addComment }) {
const [optimisticComments, addOptimisticComment] = useOptimistic(comments, (currentComments, newComment) => [
...currentComments,
{ id: Date.now(), text: newComment, isPending: true },
]);
async function handleAddComment(text) {
addOptimisticComment(text); // Сразу показываем комментарий
try {
await addComment(text); // Отправляем на сервер
} catch (error) {
console.error('Ошибка добавления комментария:', error);
// Здесь можно откатить изменения, если нужно
}
}
return (
<>
<button onClick={() => handleAddComment('Новый комментарий')}>
Добавить комментарий
</button>
<ul>
{optimisticComments.map((comment) => (
<li
key={comment.id}
style={{ opacity: comment.isPending ? 0.5 : 1}}>
{comment.text} {comment.isPending && '(ожидание)'}
</li>
))}
</ul>
</>
);
}
// Пример функции addComment
async function addComment(text) {
await fetch('/api/comments', {
method: 'POST',
body: JSON.stringify({ text }),
});
}
Новый комментарий появляется в списке сразу после клика, с полупрозрачностью и пометкой «(ожидание)», пока сервер не подтвердит операцию. Если запрос завершится успешно, UI уже готов. Если нет — можно добавить логику отката, например, удалить комментарий из списка.
Когда использовать
Для операций, где важна моментальная обратная связь — добавление лайков, комментариев, удаление записей.
Когда задержка сервера заметна, но мы хотим создать иллюзию мгновенной реакции.
В сочетании с Actions для форм с мутациями данных.
Плюсы
Делает интерфейс более отзывчивым, улучшая восприятие скорости.
Упрощает реализацию оптимистичных обновлений без сложной логики.
Гибкость в управлении временным состоянием через коллбэк.
Минусы
Требует ручной обработки ошибок и откатов при неудачных операциях.
Может запутать пользователя, если сервер часто возвращает ошибки.
Добавляет сложность в UX-дизайн для согласования оптимистичного и реального состояния.
Общие плюсы
Меньше бойлерплейта с рефами.
Новые хуки упрощают работу с формами и UX.
Код становится чище и читаемее.
Общие минусы
Придётся обновить существующие компоненты, использующие forwardRef.
Новые хуки пока не интуитивны для всех разработчиков.
Чего ждём от React
React движется к философии «пиши меньше, делай больше». Ждём:
полную интеграцию компилятора;
улучшение серверных возможностей и кеширования;
лучший DX с новыми инструментами и сообщениями об ошибках;
более глубокую совместимость с веб-компонентами.
Стоит ли переходить
В новых проектах — однозначно да. В старых — лучше потестировать в песочнице и оценить выгоды. Хотя React 19 обещает плавный переход с помощью codemods и документации.
Что в итоге
React 19 — это эволюция, которая делает разработку удобнее и быстрее. Да, есть нюансы, но преимущества перевешивают. Пробовали ли вы новые фичи? Делитесь опытом в комментариях! Пишите крутой код и до встречи!
Больше полезного про веб-разработку — в Telegram-канале Surf Web Team
Кейсы, лучшие практики, новости и вакансии в команду Web Surf в одном месте. Присоединяйтесь!
*Компания Meta Platforms Inc. (Facebook и Instagram) признана экстремистской организацией, ее деятельность на территории России запрещена.