Przejdź do treści

Astro View Transitions — płynne przejścia między stronami bez budowania SPA

Płynne przejścia jak w SPA, ale bez budowania SPA — View Transitions API w Astro i kiedy faktycznie warto go używać w produkcji.

Maciej Sala

Founder StriveLab

7 min czytaniaOpublikowano 24 kwietnia 2026 (Aktualizacja 31 maja 2026)

Tradycyjne — każde kliknięcie to pełny reload. Szybko, przewidywalnie, ale wizualnie szorstko. Astro łączy oba światy: animowane przejścia jak w , prosta architektura MPA, zero kompromisów wydajnościowych.

Skąd wzięło się View Transitions API?

View Transitions to natywne przeglądarki, które pozwala animować przejścia między stanami DOM. Początkowo było zaimplementowane tylko w Chrome (2023), potem dołączyło Safari (17.4+, marzec 2024), a Firefox dodał natywne wsparcie dopiero w wersji 144 (październik 2025) — i to najpierw dla przejść w obrębie jednego dokumentu. Dla przeglądarek bez natywnego API Astro robi — zwykły reload strony, bez animacji, ale też bez błędów.

Astro było jednym z pierwszych frameworków, które zintegrowały View Transitions w sposób end-to-end. Do Astro 4 komponent nazywał się <ViewTransitions />. W Astro 5.0 został przemianowany na <ClientRouter />, żeby lepiej odzwierciedlać zakres działania (to klient-side router, który wykorzystuje View Transitions tam, gdzie to możliwe) — stara nazwa działała jeszcze jako alias z ostrzeżeniem o wycofaniu. Astro 6.0 usunęło <ViewTransitions /> całkowicie, więc na bieżących wersjach stosujemy <ClientRouter />.

Setup Astro View Transitions w trzech liniach kodu

Żeby dodać View Transitions do całej strony, dodajesz jedną rzecz w layoucie:

Code
---
// src/layouts/Layout.astro
import { ClientRouter } from 'astro:transitions';
---
 
<html lang="pl">
<head>
  <meta charset="UTF-8" />
  <title><slot name="title">StriveLab</slot></title>
  <ClientRouter />
</head>
<body>
  <slot />
</body>
</html>

Po dodaniu <ClientRouter /> Astro automatycznie przechwytuje kliknięcia w linki wewnętrzne i zamiast pełnego reloadu robi animowane przejście z wykorzystaniem View Transitions API (jeśli przeglądarka wspiera) lub soft-reload (jeśli nie).

To wszystko. Bez zmian w stronach, bez konfiguracji, bez bibliotek zewnętrznych.

Jak Astro View Transitions działają pod maską?

Kiedy user klika link wewnętrzny:

  1. Astro przechwytuje kliknięcie przez event listener.
  2. Pobiera nową stronę przez fetch().
  3. Uruchamia document.startViewTransition() (lub fallback).
  4. Podmienia <body> na nową treść.
  5. Przeglądarka animuje przejście — domyślnie .

Efekt — user widzi płynne przejście zamiast białego błysku reloadu. Nawigacja wstecz/do przodu w przeglądarce też działa z animacjami.

Named transitions i precyzyjne animacje elementów

Sam cross-fade jest fajny, ale prawdziwą wartość View Transitions zyskują, kiedy dodajesz named transitions — oznaczone elementy, które mają animować między stronami w skoordynowany sposób.

Weźmy typowy przykład — lista artykułów i strona pojedynczego artykułu. Chcesz, żeby po kliknięciu miniaturka obrazu „powiększyła się" do pełnowymiarowego obrazu hero na nowej stronie.

Code
---
// src/pages/blog/index.astro - lista artykułów
import { getCollection } from 'astro:content';
 
const posts = await getCollection('blog');
---
 
<ul>
  {posts.map((post) => (
    <li>
      <a href={`/blog/${post.id}/`}>
        <img
          src={post.data.image}
          alt={post.data.title}
          transition:name={`hero-${post.id}`}
        />
        <h2 transition:name={`title-${post.id}`}>{post.data.title}</h2>
      </a>
    </li>
  ))}
</ul>
Code
---
// src/pages/blog/[...slug].astro - strona artykułu
const { post } = Astro.props;
---
 
<article>
  <img
    src={post.data.image}
    alt={post.data.title}
    transition:name={`hero-${post.id}`}
  />
  <h1 transition:name={`title-${post.id}`}>{post.data.title}</h1>
</article>

Kluczem do sukcesu jest to samo transition:name na obu stronach. Przeglądarka widzi, że element hero-post-123 istnieje na stronie A i B, i automatycznie animuje przejście między pozycjami i rozmiarami.

Dla bloga ze zdjęciami efekt jest natychmiastowy: tapnąć miniaturkę, płynna ekspansja do pełnego hero artykułu. Wizualnie nieodróżnialne od natywnej aplikacji iOS/Android.

Persistent state i zachowanie stanu przy nawigacji

Kolejny problem nawigacji MPA — tracisz stan elementów pomiędzy stronami. Formularz kontaktowy, audio player, kontrolki video — wszystko resetuje się przy reloadzie.

Astro daje transition:persist:

Code
<!-- src/layouts/Layout.astro -->
<audio controls transition:persist="audio-player">
  <source src="/podcast.mp3" type="audio/mpeg" />
</audio>
 
<slot />

Element oznaczony transition:persist nie jest zniszczony między stronami — zachowuje swój stan (pozycja odtwarzania, focus, wartość inputa). User może słuchać podcastu w headerze i przechodzić między artykułami bez przerywania odtwarzania.

Typowe zastosowania to player audio lub video, formularze wieloetapowe (jeśli nie zmieniają się między stronami), widgety czatu i koszyk w e-commerce — wszędzie tam, gdzie reset stanu przy nawigacji jest prawdziwym problemem użytkownika.

Dyrektywy animacji w Astro View Transitions

Domyślny cross-fade działa w większości przypadków, ale możesz wybrać inny preset:

Code
<main transition:animate="slide">
  <slot />
</main>

Dostępne presety:

  • fade — cross-fade (domyślny)
  • slide — wsunięcie z prawej/lewej
  • none — brak animacji
  • Custom — własna animacja CSS

Custom animation:

Code
---
import { fade } from 'astro:transitions';
 
const customFade = fade({ duration: '0.4s' });
---
 
<main transition:animate={customFade}>
  <slot />
</main>

Albo całkowicie własna, z keyframe'ami:

Code
@keyframes slideInFromRight {
  from {
    transform: translateX(50px);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}
 
@keyframes slideOutToLeft {
  from {
    transform: translateX(0);
    opacity: 1;
  }
  to {
    transform: translateX(-50px);
    opacity: 0;
  }
}
 
::view-transition-old(root) {
  animation: slideOutToLeft 0.3s ease-in;
}
 
::view-transition-new(root) {
  animation: slideInFromRight 0.3s ease-out;
}

Selektory ::view-transition-old i ::view-transition-new to natywne API przeglądarki — możesz stylować przejścia jak każdy inny element CSS.

Lifecycle events w Astro View Transitions

ClientRouter emituje eventy, które pozwalają reagować na nawigację:

Code
<script>
  // Przed rozpoczęciem nawigacji
  document.addEventListener('astro:before-preparation', (e) => {
    console.log('Going to', e.to)
  })
 
  // Po załadowaniu nowej strony
  document.addEventListener('astro:page-load', () => {
    console.log('Page loaded')
    // Re-init third-party scripts, analytics etc.
  })
 
  // Po animacji przejścia
  document.addEventListener('astro:after-swap', () => {
    console.log('DOM updated')
  })
</script>

To przydaje się szczególnie przy re-inicjalizacji skryptów analitycznych (GA, Plausible) po nowej stronie, re-aplikowaniu motywu (dark/light) po nawigacji i trackingu konwersji.

Pułapki i ograniczenia View Transitions

1. Scroll position. Domyślnie Astro resetuje scroll do góry przy nawigacji, ale jeśli chcesz zachować pozycję scrollowania (np. przy kliknięciu w sekcję w tej samej stronie), musisz to obsłużyć ręcznie.

2. Skrypty globalne. Skrypty w <head> ładowane są raz. Jeśli zainicjowałeś coś (np. Intersection Observer) na stronie A, po przejściu na B trzeba to zainicjować ponownie — stąd event astro:page-load.

3. Forms. ClientRouter obsługuje formularze GET i POST, ale przy krytycznych flow (płatność, logowanie, upload pliku) często lepiej dodać data-astro-reload, żeby zachować klasyczne zachowanie przeglądarki.

4. Third-party widgets. Widgety wstrzykujące treść do DOM (np. komentarze Disqus, social embeds) często nie lubią podmiany <body>. Oznacz je transition:persist lub reinicjalizuj na astro:page-load.

5. Przeglądarki bez pełnego wsparcia. Astro ma strategie fallbacku dla przeglądarek bez natywnego View Transitions API. pozostaje poprawny, ale animacje nie muszą być identyczne między silnikami.

Dostępność i skrypty po nawigacji w Astro

View Transitions wyglądają jak warstwa wizualna, ale technicznie zmieniają sposób, w jaki strona reaguje po kliknięciu linku. Dlatego wdrożenie powinno mieć krótką lista kontrolną dostępności i utrzymania skryptów.

Każda strona musi mieć sensowny <title> i główny <h1>, bo ClientRouter używa ich do ogłaszania zmiany trasy technologiom wspomagającym — bez tego użytkownik czytnika ekranu nie wie, że nawigacja w ogóle nastąpiła. Astro respektuje , ale własne animacje CSS też muszą mieć wariant ograniczonego ruchu — systemowe preferencje użytkownika powinny wygrywać z estetyką strony. Skrypty inicjalizujące obserwatory, tooltipy, galerie albo analytics uruchamiaj po astro:page-load, a nie tylko przy pierwszym ładowaniu dokumentu. Elementy z dużym stanem (player audio, koszyk) oznaczaj transition:persist tylko wtedy, gdy naprawdę mają przetrwać zmianę strony. Linki do krytycznych procesów, paneli zewnętrznych i formularzy transakcyjnych oznacz data-astro-reload.

SEO a Astro View Transitions

Obawa, która regularnie wraca: „Skoro Astro podmienia <body> przez JS, czy Google widzi wszystko?"

Odpowiedź: tak, widzi. View Transitions działają po załadowaniu statycznego HTML-a dla docelowej strony. Jeśli Google crawler wchodzi bezpośrednio na /blog/moj-wpis/, dostaje pełny, statyczny HTML — dokładnie tak samo, jakby ClientRouter nie istniał. Animacje są tylko warstwą UX dla użytkownika klikającego linki wewnętrzne.

Więcej o w Astro piszę w osobnym artykule.

Kiedy nie używać View Transitions?

View Transitions to dobre narzędzie, ale nie każde środowisko na nie zasługuje. Jeśli strona to głównie workflow z formularzami wieloetapowymi, ClientRouter komplikuje stan bardziej, niż wart jest efekt wizualny — w takim przypadku lepiej rozważyć czysty SPA (React/Next.js) albo Astro bez ClientRouter. Podobnie gdy wiele komponentów dzieli stan: MPA + View Transitions to antywzorzec, a nie usprawnienie. ClientRouter waży ~4 KB JS, więc dla landing page'a z jednym CTA, gdzie każdy kilobajt jest na wagę złota, można go spokojnie pominąć.

Dla bloga, portfolio, dokumentacji, strony firmowej czy lekkiego e-commerce View Transitions są wygraną bezsporną — i to jedną z tych rzadkich, gdzie dodanie funkcji jednocześnie poprawia wydajność postrzeganą przez użytkownika.

Astro View Transitions vs SPA

Pytanie, które dostaję regularnie: „Skoro Astro + View Transitions daje płynne przejścia, to po co w ogóle SPA?"

Odpowiedź zależy od charakteru aplikacji. Jeśli to treść z nawigacją, w której strony są de facto niezależne — Astro z View Transitions jest prostsze, szybsze i wydajniejsze niż Next.js SPA. Jeśli to aplikacja z dużym, współdzielonym state'em, gdzie użytkownik spędza czas w jednej „sesji" — SPA nadal ma przewagę.

Szczegółowe porównanie obu podejść znajdziesz w artykule o Astro vs Next.js.

Ultraszybkie projekty, łączące lekkość ze skalowalnością.
Astro

Często zadawane pytania

Czy View Transitions działają na iOS Safari?

Tak, od Safari 17.4 (marzec 2024). Starsze wersje iOS dostają graceful fallback — klasyczną nawigację bez animacji, bez błędów. Użytkownik nie odczuwa degradacji, tylko brak efektu wizualnego.

Czy mogę używać View Transitions w istniejącym projekcie Astro?

Tak. Dodanie <ClientRouter /> do layoutu jest non-destructive. Strony bez atrybutów transition:* dostaną domyślny cross-fade. Named transitions możesz dodawać stopniowo, tam gdzie mają realny wpływ na orientację użytkownika.

Czy View Transitions wpływają na performance?

ClientRouter to około 4 KB JS gzipped. Animacje są natywne w przeglądarce — CPU use jest minimalny. Perceived performance zwykle rośnie: płynne przejście jest postrzegane jako szybsze niż reload, nawet gdy czas do pierwszego paint jest identyczny.

Czy można wyłączyć View Transitions dla konkretnych linków?

Tak, dodaj atrybut data-astro-reload do linku, który ma robić pełny reload zamiast animowanego przejścia:`html<a href="/classic-page/" data-astro-reload>Klasyczna nawigacja</a>`

Czy View Transitions działają z anchorami (`#section`)?

Tak, Astro obsługuje anchor linki — przejście do sekcji na tej samej stronie animuje scroll, a między stronami scroll pozycjonuje się do elementu o odpowiednim ID po nawigacji.

O autorze

Maciej Sala

Maciej Sala — Product Manager i Frontend Developer z bogatym doświadczeniem w marketingu internetowym oraz SEO. Na co dzień pracuje z Reactem, Next.js i TypeScriptem, a ostatnio także z Astro i narzędziami do automatyzacji procesów AI. Sprawnie łączy perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat był związany z branżą gier wideo jako project manager i game designer. Absolwent historii na Uniwersytecie Jagiellońskim oraz studiów podyplomowych z marketingu internetowego na AGH w Krakowie. Po godzinach trenuje na siłowni, maluje figurki i rozwijam własne projekty.

Pomagam przekładać takie tematy na konkretne wdrożenia w frontendzie, SEO, analityce i procesie produktowym.

Skontaktuj się ze mną

Biblioteka wiedzy na temat Astro

Czytaj dalej

Zobacz więcej wpisów
Jak zdobyć 100/100 w Lighthouse z Astro? Case Study

Astro startuje z przewagą, na którą w Next.js trzeba zapracować: domyślnie wysyła zero JavaScriptu, więc świeży projekt często ma Lighthouse powyżej 95 bez jednej optymalizacji. Droga od wyniku „prawie 100” do perfekcyjnego „100/100” w PageSpeed to kwestia detali i dlatego pokazuję, jak zoptymalizować obrazy, fonty oraz skrypty zewnętrzne, i czy gra jest warta świeczki.

Maciej Sala

Maciej Sala

Founder StriveLab

Astro vs Next.js w 2026: Porównanie frameworków

Astro czy Next.js? Wybór frameworka musi być dokładnie przemyślany, zanim pojawi się pierwszy commit. Jeśli stoisz przed takim właśnie wyborem, w tym artykule staram się wykazać, w jakich obszarach najlepiej sprawdza się Astro , a w jakich będzie dominował Next.js .

Maciej Sala

Maciej Sala

Founder StriveLab

Wieloframeworkowa architektura wysp w Astro: React, Vue i Svelte w jednym projekcie

Zero JavaScript by default to chwytliwy slogan. Architektura, którą on opisuje, to coś większego: cały projekt może być statycznym HTML, a interaktywność — precyzyjnie rozmieszczonymi wyspami. Każda wyspa to niezależna jednostka z własnym frameworkiem, własnym cyklem hydratacji i własnym kosztem runtime. React, Vue, Svelte, Solid — na jednej stronie, pod jedną konfiguracją Astro. To nie eksperyment — to strategia migracji i narzędzie dla organizacji, które nie mogą sobie pozwolić na przepisanie wszystkiego od zera.

Maciej Sala

Maciej Sala

Founder StriveLab