О дивный новый анимированный мир — ViewTransition в React
Всем привет. Я Артем Курочкин, frontend разработчик компании DD Planet.
Сегодня я расскажу об одном из ключевых нововведений в React, представленных на React Conf 2025. Прошу любить и жаловать - нативная поддержка view transition api в экосистеме реакта.
Что это значит для React-разработчиков и как нам всем это поможет, - мы и разберем в этой статье.
Что за зверь такой View Transition API
The View Transition API provides a mechanism for easily creating animated transitions between different website views. This includes animating between DOM states in a single-page app (SPA), and animating the navigation between documents in a multi-page app (MPA).
Если не углубляться в подробности и сказать простым языком, данное API позволяет нам делать красивые анимации просто подключив это апи. Больше не нужно проводить часы вытирая слезы, покадрово выверяя анимацию, отрисованную дизайнером! Правда ведь? Ну, к этому мы вернемся в практической части.
Что касается всего это чуда и можно ли это тащить в прод.

И да, и нет. Все зависит от ваших требований. Хром вот поддерживает с 23 года, в то время как Firefox только в прошлом месяце включили флаг поддержки по умолчанию. Но тем не менее это baseline 2025, поэтому еще годик-два и все будет, а изучить стоит уже сейчас.
И да, дела с MPA похуже, но мы все же говорим в контексте React разработки.
Ну и в целом, забегая вперед, стейбл релиз в React произойдет лишь в 19.3, а пока мы посмотрим на практике, как это все работает в canary ветке.
Как с этим работать в React
Рассмотрим самый базовый пример как все это завести, чтобы было красиво.
В React добавили компонент ViewTransition, который является оберткой для ванильного ViewTransition интерфейса. Главное правило, без которого ничего не будет, - в нем обязательно должен лежать DOM-элемент.

Ну все, погнали в прод? Ну нет, чтобы анимация случилась, необходимо обернуть функцию, обновляющую DOM, в метод startTransition.

И вот теперь все полетит. Но есть исключение: когда мы добавляем ноду ViewTransition, воспроизводится enter-анимация, а при удалении - exit.

Но при мапе ViewTransition все же нужно вызывать startTransition.
А теперь давайте рассмотрим реальный пример, для этого я написал простую тудушку.
Вот код который реализует это:


В целом ничего сложного: один компонент обертка и вызов метода на запуск transition, а на выходе достойный результат.
Главное - не заигрывайтесь с анимациями и будьте внимательны, не вся верстка будет идеально работать.
Например, я ради эксперимента решил посмотреть, что будет с инпутом. Ответ - ничего хорошего. Даже если поставить время анимации 1мс, инпут будет терять интерактивность сильно дольше.
А что касается неидеальной работы, вот такие бывают артефакты :) (хотя, может, кому-то понравится)
Но это все цветочки, так или иначе это все можно было реализовать, написав кучку кода. А теперь перейдем к ягодкам.
Киллер фича
Барабанная дробь. Теперь можно анимировать переходы между страницами (да, стейт роутера тоже прекрасно анимируется). На этом у меня все, можно уходить (шутка).
А если серьезно, то задача такого рода (а никогда не знаешь, чего ждать от дизайнеров), ну, если раньше была не нереализуема, то точно на грани добра и зла. То теперь, пожалуйста, задача решается в одну строчку кода.
Весь код, чтобы достичь такого результата:

Да, вы все правильно поняли, просто обернуть роутер в ViewTransition.
Так или иначе, кому-то будет мало простой cross-fade анимаций. Неужели это все, что можно выжать из данного инструментария? Ответ: конечно нет!
Кастомизация
Вся кастомизация сводится к работе с CSS (да, все же его пописать придется, если хочется чего-то большего)
View Transition API вводит новые псевдо-элементы:

А у компонента ViewTransition есть пропсы, которые позволяют задать класс для общения с этими псевдо-элементами.

P.S. Там еще колбеки есть на enter, exit, update и share, но я не придумал им практического применения. Можете поделиться своими идеями в комментариях.
Ну так вот, вернемся к практике. Я немного поигрался с кастомизацией анимации тудушек.
Вот все телодвижения, что я совершил для этого:


И отвечая на вопрос, который мог возникнуть у многих, кто сталкивался с анимациями: да, нас услышали - больше возится с тем, чтобы удалить элемент из DOMа только по завершению анимации, не нужно.
Никаких больше CSSTransition и тому подобных либ, или что еще хуже, самим прописывать setTimeout-ы.
Если технически углубится, элементы теперь уничтожаются сразу, но появляется псевдо-элемент view-transition, в котором анимация и воспроизводится. C чем связан и неприятный бонус: к сожалению, пока воспроизводится анимация, взаимодействие с интерфейсом блокируется. Так что нужно соблюдать тонкую грань между плавностью/красотой и user friendly.
И добавлю, что для enter и exit не имеет смысла прописывать какие либо псевдо-элементы кроме new и old соответственно.
И если немного “пошаманить с бубном”, можно получить красивые переходы между страницами.


Но и это не все, что мы можем накрутить. Можно выбирать разные анимации в зависимости от действий юзера, так как в пропсы можно передавать не только строку, но и “словари“. Чтобы переключить тип анимации, нужно использовать метод addTransitionType.
А вот и реализация:


Fallbacks и оптимизация
Не нашел этому место в основном рассказе, но также можно сделать красивые переходы между фолбеком и загрузившимся контентом.
Можно использовать для этого update или enter/exit. Реализуется это так:

И да, если у вас есть анимированный родитель, но у ребенка нужно убрать анимацию, в целях оптимизации или просто потому что, ребенка достаточно обернуть в еще один ViewTransition и в update прописать класс “none”:

Заключение
В целом у меня очень большие ожидания от данного API, и я очень рад, что это завезут в свежем React. Оно открывает двери в мир, где анимации реализуются на чистом CSS (почти), так как проблема с DOM ушла. Ну а для простеньких так и в CSS лезть не надо, все из коробки: обернул что нужно, накинул методы для запуска и красиво.
Особенно это выделяется среди непонятного Activity и откровенного гениального костыльного решения проблемы ESlint в виде useEffectEvent, которые нас также ждут в React 19.3.
Ну а на этом у меня все. Всем добра и позитива. Пишите хороший код.