Реалізація CSS View Transitions API для переходів між сторінками

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація CSS View Transitions API для переходів між сторінками
Середня
від 1 робочого дня до 3 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація CSS View Transitions API для переходів між сторінками

View Transitions API дозволяє створювати плавні анімовані переходи між станами DOM — або між сторінками в MPA/SPA — використовуючи нативний браузерний механізм. Браузер робить скріншот поточного стану, застосовує новий, а потім анімує перехід. За замовчуванням використовується crossfade, але через CSS-псевдоелементи можна реалізувати будь-яку анімацію.

Підтримка браузерів

Chrome 111+, Edge 111+, Safari 18+. Firefox — з флагом, без стабільної підтримки у 2025 році. Обов'язково перевірити document.startViewTransition перед використанням.

Базове використання (SPA)

// utils/view-transition.ts

export async function navigateWithTransition(
  updateDOM: () => void | Promise<void>
): Promise<void> {
  // Fallback для браузерів без підтримки
  if (!document.startViewTransition) {
    await updateDOM()
    return
  }

  const transition = document.startViewTransition(async () => {
    await updateDOM()
  })

  try {
    await transition.finished
  } catch (e) {
    // Перехід був переривний (почався новий перехід)
    if (!(e instanceof DOMException && e.name === 'AbortError')) {
      throw e
    }
  }
}

Інтеграція з React Router / Next.js

React Router v6

// router/transition-router.tsx
import { useNavigate } from 'react-router-dom'
import { navigateWithTransition } from '../utils/view-transition'

export function useTransitionNavigate() {
  const navigate = useNavigate()

  return (to: string, options?: { replace?: boolean }) => {
    navigateWithTransition(() => {
      navigate(to, options)
    })
  }
}

Next.js App Router

Next.js 14+ підтримує View Transitions через unstable_viewTransition в next/link:

// app/components/TransitionLink.tsx
'use client'
import Link from 'next/link'
import { useRouter } from 'next/navigation'

export function TransitionLink({
  href,
  children,
  className,
}: {
  href: string
  children: React.ReactNode
  className?: string
}) {
  const router = useRouter()

  const handleClick = (e: React.MouseEvent) => {
    e.preventDefault()
    if (!document.startViewTransition) {
      router.push(href)
      return
    }
    document.startViewTransition(() => {
      router.push(href)
    })
  }

  return (
    <a href={href} onClick={handleClick} className={className}>
      {children}
    </a>
  )
}

CSS: користувацькі анімації переходів

/* styles/view-transitions.css */

/* Псевдоелементи за замовчуванням */
::view-transition-old(root) {
  animation: fade-out 0.2s ease-out;
}

::view-transition-new(root) {
  animation: fade-in 0.3s ease-out;
}

@keyframes fade-out {
  to { opacity: 0; }
}

@keyframes fade-in {
  from { opacity: 0; }
}

/* Слайд для навігації */
@keyframes slide-from-right {
  from { transform: translateX(40px); opacity: 0; }
}

@keyframes slide-to-left {
  to { transform: translateX(-40px); opacity: 0; }
}

/* Застосовуємо до конкретного напрямку (атрибут на html) */
html[data-transition="forward"] {
  &::view-transition-old(root) {
    animation: slide-to-left 0.25s ease-in both;
  }
  &::view-transition-new(root) {
    animation: slide-from-right 0.25s ease-out both;
  }
}

html[data-transition="backward"] {
  &::view-transition-old(root) {
    animation: 0.25s ease-in both reverse slide-from-right;
  }
  &::view-transition-new(root) {
    animation: 0.25s ease-out both reverse slide-to-left;
  }
}

Встановлення атрибуту при навігації:

// Визначаємо напрямок перед стартом переходу
function navigate(to: string, history: string[]) {
  const isBack = history[history.length - 2] === to
  document.documentElement.dataset.transition = isBack ? 'backward' : 'forward'

  document.startViewTransition(() => {
    // Оновлення DOM/маршруту
    router.push(to)
  })
}

Названі view-transition-name: спільні елементи

Названі переходи дозволяють плавно "переносити" елемент між сторінками (shared element transition):

/* Сторінка списку */
.product-card-image {
  view-transition-name: product-image; /* унікальне ім'я */
}

/* Сторінка детального перегляду */
.product-detail-image {
  view-transition-name: product-image; /* те ж ім'я */
}

/* Браузер сам анімує перехід від одного до іншого */
::view-transition-group(product-image) {
  animation-duration: 0.4s;
  animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

Для динамічних імен (список товарів):

// components/ProductCard.tsx
export function ProductCard({ product }: { product: Product }) {
  return (
    <div>
      <img
        src={product.image}
        alt={product.name}
        style={{
          // view-transition-name повинно бути унікальним у DOM
          viewTransitionName: `product-image-${product.id}`,
        }}
      />
    </div>
  )
}
// pages/product/[id]/page.tsx
export default function ProductDetail({ params }: { params: { id: string } }) {
  return (
    <img
      src={product.image}
      style={{ viewTransitionName: `product-image-${params.id}` }}
    />
  )
}

Керування паузою та готовністю

async function transitionWithControl(update: () => void) {
  const transition = document.startViewTransition(update)

  // Чекаємо поки старий контент скопійований
  await transition.ready

  // Тут можна запустити додаткові Web Animations
  document.documentElement.animate(
    { opacity: [1, 0.8, 1] },
    { duration: 300, pseudoElement: '::view-transition-new(root)' }
  )

  await transition.finished
}

Відключення для prefers-reduced-motion

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none;
  }
}

Типові терміни

Базовий crossfade між сторінками Next.js/React Router — 4–6 годин. Спрямовані слайд-переходи + shared element transitions для списку/деталей — 2–3 робочих дні. Повна система з керуванням напрямком, fallback, тестами та підтримкою Safari — 3–4 робочих дні.