Официальный релиз библиотеки React 19.3, опубликованный в реестре npm, перевел в стабильный статус сразу две долгожданные возможности архитектуры: компонент <ViewTransition> и поддержку ссылок (ref) для фрагментов. Обе концепции находились на стадии экспериментов в Canary-каналах около года, а теперь стали частью общедоступного API для веб-приложений.
Нововведения решают две давние боли фронтенд-инженеров: создание плавных морфинг-анимаций между экранами без тяжелых сторонних анимационных библиотек и чистое управление группами DOM-узлов без добавления лишних тегов-оберток.
Декларативные анимации переходов: компонент <ViewTransition>
До появления компонента <ViewTransition> интеграция браузерного View Transition API в React требовала громоздких императивных вызовов document.startViewTransition() внутри хуков useEffect или хаков с ручным управлением жизненным циклом компонентов. Это часто приводило к рассинхронизации виртуального DOM и реальных экранных снимков браузера.
Компонент <ViewTransition> предоставляет полностью декларативную модель. Он связывает нативные анимации переходов браузера с механизмом конкурентного рендеринга React.
Ключевое архитектурное правило: компонент <ViewTransition> активирует анимации только тогда, когда изменение состояния обернуто в переход — через startTransition, хук useDeferredValue или компонент <Suspense>. Срочные синхронные обновления намеренно игнорируют анимацию, чтобы интерфейс не терял отзывчивости при вводе текста или быстрых кликах.
В жизненном цикле перехода компонент выделяет четыре фазы:
enter— появление нового элемента в DOM-дереве;exit— удаление элемента из структуры;update— изменение геометрических размеров или позиции узла;share— разделяемый переход между двумя разными компонентами на разных экранах (например, превращение превью карточки товара в полноэкранный заголовок).
Ниже представлен первый пример реализации: базовое переключение табов каталога с плавной анимацией через <ViewTransition>:
import React, { useState, startTransition } from 'react';
import { ViewTransition } from 'react';
export function TabSelector({ categories }) {
const [activeTab, setActiveTab] = useState(categories[0]);
const handleSelect = (category) => {
// Обязательное условие: оборачиваем обновление в transition
startTransition(() => {
setActiveTab(category);
});
};
return (
<div className="tab-container">
{categories.map((cat) => (
<button key={cat.id} onClick={() => handleSelect(cat)}>
{cat.name}
{activeTab.id === cat.id && (
// Декларативный индикатор активного таба плавно перемещается
<ViewTransition name="tab-indicator">
<span className="active-pill" />
</ViewTransition>
)}
</button>
))}
</div>
);
}
Ссылки на фрагменты: избавление от оберточных div
Вторым важным новшеством React 19.3 стала нативная поддержка ref на фрагментах <React.Fragment> (и в сокращенном синтаксисе <>...</>).
Исторически фрагменты использовались исключительно для группировки соседних JSX-элементов без создания промежуточного DOM-узла. Однако если родительскому компоненту требовалось измерить суммарные границы группы элементов (например, для позиционирования тултипа или виртуализации списка), разработчикам приходилось оборачивать группу в настоящий <div>. Это нарушало сетки CSS Grid, ломало flexbox-контейнеры и загрязняло верстку.
В React 19.3 ссылка, переданная во фрагмент, предоставляет доступ к коллекции реальных дочерних DOM-узлов или к их объединенному ограничивающему прямоугольнику (Bounding Box).
Развивая наш сценарий, второй блок кода показывает, как объединить анимированный переход <ViewTransition> с измерением группы элементов через Fragment Ref без единого лишнего узла разметки:
import React, { useRef, useLayoutEffect, useState, startTransition } from 'react';
import { ViewTransition } from 'react';
export function AnimatedProductGrid({ items }) {
const [selectedGroup, setSelectedGroup] = useState('all');
const fragmentRef = useRef(null);
useLayoutEffect(() => {
if (fragmentRef.current) {
// Получаем доступ к границам группы узлов фрагмента напрямую
const domNodes = fragmentRef.current.getNodes?.() || [];
console.log(`Отрендерено элементов во фрагменте: ${domNodes.length}`);
}
}, [selectedGroup]);
return (
<div className="catalog-grid">
<button onClick={() => startTransition(() => setSelectedGroup('featured'))}>
Показать избранное
</button>
{/* Фрагмент принимает ref и не вносит лишний div в CSS Grid */}
<React.Fragment ref={fragmentRef}>
{items.map((item) => (
<ViewTransition key={item.id} name={`product-card-${item.id}`}>
<article className="product-card">
<h3>{item.title}</h3>
<p>{item.price} ₽</p>
</article>
</ViewTransition>
))}
</React.Fragment>
</div>
);
}
Поддержка компонента <ViewTransition> в релизе React 19.3 стабилизирована исключительно для веб-платформы (React DOM). В мобильном рантайме React Native нативная поддержка переходов экранов находится на этапе проектирования спецификации и пока недоступна в стабильных сборках.
Стабилизация <ViewTransition> и ссылок на фрагменты в React 19.3 делает декларативную работу с браузерными визуальными стандартами естественной частью экосистемы фреймворка, сокращая объем вспомогательного JavaScript-кода и очищая DOM-структуру приложений.
