View Transitions API в Astro: реальные кейсы
View Transitions появились в Astro ещё в третьей версии и за это время превратились из красивой демки в рабочий инструмент. Я их использую в трёх проектах: сайте конференции, корпоративном блоге и интернет-магазине одежды. Везде по-разному — где-то это просто плавный fade между страницами, где-то — анимация перетекающего изображения с карточки в детальную.
В этой заметке покажу четыре реальных кейса с кодом и оговорками. Ничего фантастического, всё работает в стабильных Chrome и Firefox 2026.
Что такое View Transitions в Astro
Это надстройка над браузерным API document.startViewTransition. Astro между навигациями делает MPA похожим на SPA: подменяет HTML без перезагрузки и анимирует переходы. Для подключения хватает одной строчки в общем layout:
---
import { ViewTransitions } from 'astro:transitions';
---
<html lang="ru">
<head>
<ViewTransitions />
</head>
<body><slot /></body>
</html>Этого достаточно для базового fade. Дальше — кастомизация по элементам.
Кейс 1. Карточка → детальная страница (shared element)
На сайте, где у каждой статьи есть обложка, хочется, чтобы при клике картинка не «прыгала» с маленькой превьюшки до большой, а плавно перетекала. Для этого обоим элементам ставим одно и то же transition:name:
<!-- список -->
<a href={`/p/${post.slug}`}>
<img
src={post.cover}
alt={post.title}
transition:name={`cover-${post.slug}`}
/>
<h2 transition:name={`title-${post.slug}`}>{post.title}</h2>
</a>
<!-- детальная -->
<img src={post.cover} alt={post.title} transition:name={`cover-${post.slug}`} />
<h1 transition:name={`title-${post.slug}`}>{post.title}</h1>На клик карточка плавно увеличивается, заголовок переезжает, всё выглядит как родное SPA. Браузеру для этого ничего больше не нужно, никакой настройки CSS. Хитрость: transition:name должен быть уникальным для пары «исходный — целевой», иначе анимация не сцепится.
Кейс 2. Кастомная анимация перехода
По умолчанию Astro делает fade. Хочется, например, slide-up для главной и обычный fade для остальных. Это решается через transition:animate:
<main transition:animate="slide">
<slot />
</main>Доступные варианты: fade (по умолчанию), slide, none и initial. Если хочется свою — описываешь через CSS-переменную:
::view-transition-old(root) {
animation: 220ms cubic-bezier(0.4, 0, 0.2, 1) both fade-out;
}
::view-transition-new(root) {
animation: 220ms cubic-bezier(0.4, 0, 0.2, 1) both slide-in-up;
}
@keyframes fade-out { to { opacity: 0; } }
@keyframes slide-in-up { from { transform: translateY(8px); opacity: 0; } }Кейс 3. Сохранение состояния (persist)
На магазине есть плеер с обзором коллекции, который не хочется перезагружать при переходе между страницами. Для этого Astro даёт директиву transition:persist:
<audio controls transition:persist="main-player">
<source src="/audio/playlist.mp3" type="audio/mpeg" />
</audio>Ключевое — одинаковое имя в transition:persist на обеих страницах. Astro переиспользует тот же DOM-узел, и плеер продолжает играть после смены маршрута. Я так же использую этот приём для верхней навигации с открытым выпадающим меню — выглядит как SPA, хотя страницы перезагружаются.
Кейс 4. Условное отключение для пользователей
Анимации не для всех. У части пользователей выставлен prefers-reduced-motion: reduce, и им нужны мгновенные переходы. Astro уважает это значение автоматически — если браузер сообщает «без движения», переходов не будет. Но если хочешь дать пользователю явный выключатель — добавь свой переключатель и хранение в cookie.
document.addEventListener('astro:before-preparation', (e) => {
if (localStorage.getItem('animations') === 'off') {
e.preventDefault();
}
});Тут e.preventDefault() отменяет View Transition, и страница откроется обычной перезагрузкой. На больших сайтах с тяжёлыми анимациями такой выключатель — заметная плюсуха к UX.
События жизненного цикла
Astro даёт несколько кастомных событий, которые удобно слушать:
astro:before-preparation— перед началом перехода;astro:after-preparation— когда новый HTML уже разобран, но ещё не вставлен;astro:before-swap— перед заменой DOM (можно подменить заголовок страницы и т. п.);astro:after-swap— DOM подменён, можно повторно проинициализировать сторонние библиотеки;astro:page-load— финальный, аналогDOMContentLoadedдля SPA-режима.
На after-swap я обычно перезапускаю прогресс-бар чтения, переинициализирую подсветку кода (Prism, Shiki) и подключаю клиентский поиск.
Тонкости
Транзишены не работают между совсем разными типами страниц. Если у тебя главная — статика, а блог — SSR с динамическим списком, переход будет, но shared elements не сработают, потому что DOM соответствующего узла в исходной странице нет.
Sticky-элементы (липкая шапка) часто моргают, потому что во время анимации они не «знают», на какой стороне находятся. Лечение — задать им transition:name с уникальным именем и явный layout: тогда браузер сам сделает корректный shared transition.
Очень тяжёлые анимации (большие изображения, много DOM-узлов) ловят кадровый jank. Если стресс-тест ругается — упрости целевой DOM или ограничь анимацию через contain: paint на родителе.
Совместимость
На январь 2026 поддержка View Transitions: Chromium-браузеры — стабильно с версии 111, Firefox — с 124, Safari — с 17.4. Везде, где не поддерживается, переход просто превращается в обычный фейд через Astro-fallback. Юзер ничего «сломанного» не увидит.
Когда не использовать
- Если сайт состоит из одной страницы — нечего переходить.
- Если у тебя SSR с длинным временем ответа: переход начнётся, а потом будет ждать сервер и выглядеть зависшим. Я в таких местах либо отключаю транзишен, либо показываю промежуточный лоадер через
loading.astro. - Если используешь сторонний роутер (например, htmx) — в нём своя логика навигации.
Что дальше
View Transitions — недешёвая в плане сил кастомизация на старте, но возвращает массу очков восприятия проекта. Раньше за плавную смену страниц приходилось платить целым SPA-фреймворком и SSR-стеком; теперь — одной строчкой и осмысленной разметкой. Для контентных сайтов это, на мой взгляд, must-have.