View Transitions Cross-Document en 2026: Guía MPA con @view-transition, pagereveal y view-transition-name

Guía práctica de View Transitions cross-document en 2026: activa @view-transition, anima elementos con view-transition-name, combínalo con Speculation Rules y depura en DevTools.

View Transitions MPA 2026: Guía Cross-Doc

Actualizado: 14 de septiembre de 2026

La View Transitions API cross-document es una interfaz web nativa que anima transiciones entre páginas HTML completas de una aplicación multipágina (MPA) usando la regla CSS @view-transition y la propiedad view-transition-name, sin necesidad de un enrutador SPA. Desde Chrome 126 (junio de 2024) y Safari 18 (septiembre de 2024) está disponible en producción. En 2026 se combina de forma natural con Speculation Rules para ofrecer navegaciones instantáneas y animadas en sitios estáticos, e-commerce y blogs, reduciendo notablemente el CLS percibido durante la navegación.

Honestamente, la primera vez que la activé en un blog estático (Astro + Netlify), sentí que había hecho trampa: dos líneas de CSS y las navegaciones dejaron de parpadear.

  • Las View Transitions cross-document se activan añadiendo @view-transition { navigation: auto; } en el CSS de ambas páginas del mismo origen; no requieren JavaScript ni un framework SPA.
  • La propiedad view-transition-name marca los elementos que deben "morph" entre páginas (por ejemplo, una imagen de héroe o un logotipo) creando una animación de posición y tamaño automática.
  • Los eventos pagereveal (nuevo documento) y pageswap (documento saliente) permiten personalizar la transición programáticamente y pasar datos entre páginas mediante ViewTransition.types.
  • Combinar View Transitions con Speculation Rules API logra navegaciones sub-100 ms con animaciones fluidas, mejorando LCP percibido y reduciendo INP en enlaces.
  • Chrome, Edge y Safari 18+ ya soportan cross-document; Firefox 130 lo tiene tras flag, con activación por defecto esperada en 2026. La degradación es transparente: los navegadores sin soporte simplemente cargan la página sin animación.
  • Los errores más frecuentes son nombres duplicados de view-transition-name, elementos ocultos con display: none durante la captura, y bloqueo de la transición por scripts largos en pagereveal.

¿Qué es la View Transitions API cross-document?

La View Transitions API cross-document es la extensión de la API estándar de transiciones de vista, especificada por el borrador CSS View Transitions Level 2 del CSSWG, que aplica transiciones animadas entre dos navegaciones HTML completas del mismo origen. A diferencia de las transiciones same-document (introducidas en 2023 con Chrome 111), que requerían llamar a document.startViewTransition() desde JavaScript en una SPA, la versión cross-document funciona con enlaces <a> tradicionales y formularios, sin ningún enrutador ni bundler intermedio.

El navegador se encarga de capturar automáticamente un snapshot del documento antiguo justo antes de la navegación, cargar el nuevo documento, capturar su snapshot y ejecutar una animación de "crossfade" (o personalizada) entre ambos. Todo esto ocurre sin bloquear el hilo principal: la animación se ejecuta en el proceso del compositor. Para sitios que ya son rápidos (SSG estáticos, Astro, Hugo, Eleventy), esta API elimina la principal razón histórica para adoptar una SPA, es decir, la sensación de "recarga completa" al navegar.

En 2026 la API es especialmente relevante porque casi todos los navegadores modernos la soportan, el patrón "MPA rápida + Speculation Rules + View Transitions" ha sustituido a muchas SPAs, y las herramientas oficiales de DevTools (panel Animations) permiten inspeccionar y depurar transiciones con facilidad. Google la considera un patrón recomendado para reducir el LCP percibido.

Cómo activar View Transitions entre páginas con @view-transition

Activar transiciones cross-document requiere una sola regla CSS. Añade el siguiente bloque al CSS global cargado por el <head> de ambas páginas involucradas (idealmente en un CSS global compartido):

/* global.css: incluido en TODAS las páginas del sitio */
@view-transition {
  navigation: auto;
}

La regla @view-transition con navigation: auto le indica al navegador que active la transición para toda navegación same-origin, same-document-type que no sea una recarga forzada. Es importante que ambas páginas (origen y destino) contengan esta regla. Si sólo una la tiene, la transición no se ejecuta y el navegador hace una navegación estándar sin animación. También hace falta que ambas páginas se sirvan por HTTPS o localhost, y que la navegación sea de tipo push (enlace, formulario o history.pushState). Las recargas y navegaciones reload no disparan la transición.

Por defecto, sin configuración adicional, verás un crossfade suave de 250 ms entre las dos páginas. Ese ya es un enorme salto cualitativo respecto al parpadeo blanco tradicional. Para desactivar la transición en rutas específicas (por ejemplo, un checkout donde no la quieres), puedes usar la variante granular con la palabra clave none:

/* Sólo transiciones para rutas /blog/* */
@view-transition {
  navigation: none;
}

/* Y luego, en el CSS específico de /blog/ */
@view-transition {
  navigation: auto;
}

Respetar prefers-reduced-motion es obligatorio para accesibilidad. Envuelve la activación en una media query:

@media not (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: auto;
  }
}

view-transition-name: animar elementos compartidos entre páginas

La verdadera magia aparece cuando aplicas view-transition-name a elementos que existen (o podrían "identificarse como el mismo elemento") en ambas páginas. El navegador los captura por separado del resto del documento y les aplica una animación de morph: interpola posición, tamaño y opacidad entre el estado inicial (página antigua) y el final (página nueva).

Ejemplo típico: una imagen de héroe en el listado de un blog que "vuela" a su posición final en el artículo:

<!-- /blog/index.html -->
<a href="/blog/post-42">
  <img src="/img/post-42.avif" alt="" style="view-transition-name: post-42-hero">
  <h2>Título del artículo</h2>
</a>

<!-- /blog/post-42.html -->
<article>
  <img src="/img/post-42.avif" alt="" style="view-transition-name: post-42-hero">
  <h1>Título del artículo</h1>
</article>

El valor de view-transition-name debe ser único dentro de cada documento. Si tienes 20 artículos en una página de listado, cada tarjeta necesita un nombre distinto (por ejemplo post-42-hero, post-43-hero). Generar el nombre desde el ID del post en tu SSG es la forma limpia de hacerlo. Para elementos comunes a todas las páginas (encabezado, logotipo, footer), un nombre fijo como site-header funciona perfectamente.

Un patrón muy útil, y que descubrí a la mala tras animar 30 miniaturas a la vez, es aplicar view-transition-name dinámicamente sólo al elemento que el usuario clicó, usando el evento click para setear un estilo inline justo antes de la navegación. Esto evita que el navegador anime 20 imágenes potenciales y mantiene la animación rápida y enfocada.

Personalizar animaciones con ::view-transition-group y pseudo-elementos

La API expone un árbol de pseudo-elementos que puedes estilar con CSS estándar (animation, transition, @keyframes). La jerarquía es:

  • ::view-transition: la raíz que cubre todo el viewport durante la transición.
  • ::view-transition-group(name): el contenedor de cada elemento nombrado; controla posición y tamaño.
  • ::view-transition-image-pair(name): envuelve las dos capturas (antigua y nueva).
  • ::view-transition-old(name): snapshot del elemento en el documento saliente.
  • ::view-transition-new(name): snapshot del elemento en el documento entrante.

Un ejemplo de crossfade más largo con curva de easing personalizada:

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 400ms;
  animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}

/* Slide horizontal para el contenido principal */
::view-transition-old(main-content) {
  animation: slide-out-left 300ms ease-in both;
}
::view-transition-new(main-content) {
  animation: slide-in-right 300ms ease-out both;
}

@keyframes slide-out-left {
  to { transform: translateX(-30%); opacity: 0; }
}
@keyframes slide-in-right {
  from { transform: translateX(30%); opacity: 0; }
}

Eventos pagereveal y pageswap: control programático

Para cargas condicionales, transferencia de estado o transiciones dinámicas, la API cross-document introduce dos eventos DOM nuevos:

  • pageswap: se dispara en el window del documento saliente justo antes de la navegación. Recibe un objeto PageSwapEvent con propiedades activation (info sobre la navegación) y viewTransition (referencia a la transición).
  • pagereveal: se dispara en el window del nuevo documento cuando este está listo para renderizar. Recibe un PageRevealEvent con viewTransition.

Ambos eventos permiten leer y modificar viewTransition.types, un Set que actúa como discriminador entre tipos de transición (por ejemplo 'forwards' vs 'backwards' vs 'reload'):

// Añadir tipos según la relación entre URL antigua y nueva
window.addEventListener('pageswap', (event) => {
  const vt = event.viewTransition;
  if (!vt) return;

  const from = event.activation.from.url;
  const to = event.activation.entry.url;

  if (isChildRoute(from, to)) {
    vt.types.add('forwards');
  } else if (isParentRoute(from, to)) {
    vt.types.add('backwards');
  }
});

window.addEventListener('pagereveal', (event) => {
  const vt = event.viewTransition;
  if (!vt) return;
  // El nuevo documento hereda los tipos vía navigation
});

Después puedes seleccionar animaciones específicas por tipo desde CSS con la pseudo-clase :active-view-transition-type():

html:active-view-transition-type(forwards) {
  &::view-transition-old(root) { animation: slide-out-left 300ms both; }
  &::view-transition-new(root) { animation: slide-in-right 300ms both; }
}
html:active-view-transition-type(backwards) {
  &::view-transition-old(root) { animation: slide-out-right 300ms both; }
  &::view-transition-new(root) { animation: slide-in-left 300ms both; }
}

Combinar View Transitions con Speculation Rules

La combinación más potente en 2026 es Speculation Rules + View Transitions cross-document. Speculation Rules pre-renderiza la página destino en un proceso oculto; cuando el usuario clica el enlace, el navegador "activa" ese pre-render con una latencia de decenas de milisegundos. Añadir View Transitions convierte esa navegación instantánea en una animación fluida.

<script type="speculationrules">
{
  "prerender": [{
    "where": { "href_matches": "/blog/*" },
    "eagerness": "moderate"
  }]
}
</script>

<style>
  @view-transition { navigation: auto; }
  .card-image { view-transition-name: attr(data-vt-name); }
</style>

El navegador ejecuta el pre-render cuando detecta intención (hover, focus, o su propio algoritmo interno según eagerness), captura el snapshot, y al hacer clic la transición ya tiene todos los assets listos. En un catálogo pequeño que medí este año con esta combinación, el LCP percibido bajó de ~1.2 s a menos de 150 ms en el p75.

Para profundizar en la configuración de Speculation Rules (incluyendo cómo evitar prerender excesivo que consume batería), revisa nuestra guía de Speculation Rules API para navegación instantánea. La sinergia entre ambas APIs también reduce el INP percibido en los clics de enlaces, ya que el navegador responde antes de que el hilo principal ejecute el handler.

Compatibilidad de navegadores en 2026

El soporte de View Transitions cross-document ha evolucionado bastante. La documentación oficial de MDN sobre la View Transitions API mantiene la tabla actualizada, pero este es el panorama en septiembre de 2026:

NavegadorSame-documentCross-document (MPA)@view-transition types
Chrome / Edge111+ (2023)126+ (junio 2024)127+
Safari18.0 (septiembre 2024)18.2 (diciembre 2024)18.4
Firefox130 tras flag; estable en 2026En Nightly, activación por defecto esperada 2026Detrás de flag
Samsung Internet25+26+26+

La degradación es transparente y no requiere detección: si el navegador no soporta @view-transition, la regla se ignora y la navegación es la habitual. Sin embargo, para evitar aplicar view-transition-name en navegadores sin soporte (donde no hace nada pero ocupa memoria), puedes usar feature detection en JavaScript:

if (
  'startViewTransition' in document &&
  CSS.supports('view-transition-name', 'test')
) {
  document.documentElement.classList.add('supports-vt');
}

Y en CSS, aplica los nombres sólo cuando la clase esté presente:

.supports-vt .card-image {
  view-transition-name: var(--vt-name);
}

Errores comunes y depuración con DevTools

Cuando una transición no se ejecuta o se ve rota, estas son las causas más frecuentes que hemos visto en producción:

  1. Nombres duplicados de view-transition-name. Si dos elementos tienen el mismo nombre en la misma página, el navegador aborta la transición completa (no sólo ese elemento). La consola de Chrome DevTools muestra el warning Aborted view transition due to duplicate view-transition-name. Inspecciona el árbol en el panel Elements y busca colisiones.
  2. Elementos con display: none o visibility: hidden durante la captura. El navegador captura sólo lo pintado. Si tu elemento con nombre está oculto en la página origen pero visible en la destino, la transición degrada a un fade simple.
  3. Navegación con recarga completa. Enlaces con target="_blank", formularios POST o navegaciones a cross-origin no disparan transiciones. Verifica el tipo de navegación en el panel Network.
  4. Scripts largos en pagereveal. Un handler que tarda más de un frame retrasa el inicio de la animación y visualmente parece un "jank". Mide con el panel Performance filtrando por "View transition".
  5. Race conditions con lazy-loading. Las imágenes con loading="lazy" que no están cargadas en la página destino aparecen como espacios en blanco durante la transición. Para imágenes participantes en transiciones, usa loading="eager" o fetchpriority="high".

Un truco que me salvó una noche entera de depuración: Chrome DevTools 128+ incluye una casilla "Capture view transition" en el panel Animations que reproduce la transición en cámara lenta y expone el árbol de pseudo-elementos. Para optimizar imágenes en transiciones críticas, combina fetchpriority="high" con formatos modernos como AVIF y precarga en la página origen.

Preguntas frecuentes

¿Qué diferencia hay entre View Transitions same-document y cross-document?

Same-document se usa dentro de una SPA llamando a document.startViewTransition(callback) desde JavaScript para animar cambios de DOM en la misma página. Cross-document funciona en MPAs con enlaces normales entre dos documentos HTML del mismo origen mediante la regla CSS @view-transition { navigation: auto; }, sin código JavaScript. Ambas comparten el mismo modelo de pseudo-elementos y view-transition-name.

¿View Transitions cross-document funciona entre orígenes distintos?

No. La especificación limita las transiciones a navegaciones same-origin por razones de seguridad (para evitar leaks de contenido visual entre orígenes). Las navegaciones a subdominios distintos, orígenes cross-site o esquemas diferentes (HTTP↔HTTPS) omiten la transición y hacen navegación estándar.

¿Las View Transitions afectan al SEO o al Core Web Vitals?

No afectan negativamente el SEO ni el LCP inicial de la primera carga, porque sólo se activan a partir de la segunda navegación. Sí mejoran el LCP percibido en navegaciones posteriores (especialmente combinadas con Speculation Rules) y pueden reducir el CLS percibido al eliminar el "flash blanco" entre páginas. Google no penaliza a los sitios que las usen.

¿Cómo desactivar una View Transition para una navegación específica?

En el evento pageswap, llama a event.viewTransition.skipTransition() para saltar la animación pero mantener la navegación. También puedes evitar activarla desde CSS con @view-transition { navigation: none; } en rutas donde no la quieras (usando @scope o media queries por URL), o interceptar el clic y usar router.navigate() tradicional.

¿Puedo usar View Transitions con frameworks como Astro, Next.js o SvelteKit?

Sí. Astro tiene soporte de primera clase desde la versión 3 con el componente <ViewTransitions /> que activa automáticamente @view-transition. Next.js 15+ soporta cross-document en el App Router cuando experimental.viewTransition está activo. SvelteKit lo integra en su router desde 2.5. En cualquier framework MPA-first, basta con añadir la regla CSS global.

Editorial Team
Sobre el Autor Editorial Team

Our team of expert writers and editors.