wandres.dev
VIEW TRANSITIONS · ClientRouter y transiciones

transition:name y transition:animate: emparejar y animar

Las dos directivas que dan forma a una view transition. Cómo transition:animate elige la animación de un elemento entre fade, slide, initial y none; cómo transition:name asigna un nombre compartido que el navegador usa para emparejar un mismo elemento entre dos páginas y hacerlo morfar en lugar de desvanecerse; la regla de unicidad de los nombres, y cómo generar nombres estables en listas.

⏱ 16 min

Con el <ClientRouter /> activo, toda navegación ocurre dentro de una view transition, y por defecto el navegador aplica un fundido suave a la página entera. Eso ya es agradable, pero el verdadero poder aparece cuando dejas de tratar la página como un bloque y empiezas a hablar de elementos concretos: este título debe deslizarse, aquella miniatura debe crecer hasta convertirse en la portada de la página siguiente. Dos directivas gobiernan ese lenguaje. transition:animate decide cómo se anima un elemento; transition:name decide qué elemento de la página nueva es la continuación de cuál de la vieja. Entender que son preguntas distintas —animación e identidad— es la mitad de la batalla.

🎯 Al terminar esta lección sabrás
  • Aplicar transition:animate con las animaciones integradas: fade, slide, initial y none.
  • Comprender que transition:name da a un elemento una identidad compartida entre páginas.
  • Emparejar dos elementos de páginas distintas para que uno morfe en el otro.
  • Respetar la regla de unicidad del nombre y generar nombres estables en listas.

transition:animate: elegir la animación de un elemento

Por defecto, la transición de página es un fundido global. Con transition:animate reasignas la animación de un elemento concreto, y con ella la de su porción de la pantalla. Astro trae varias integradas. fade funde el elemento saliente con el entrante. slide lo desliza —hacia fuera por un lado y hacia dentro por el otro—, y es sensible a la dirección: en un salto hacia delante se desliza en un sentido y al pulsar atrás lo hace en el contrario, reforzando la sensación de ir y volver. initial deja la animación por defecto del navegador, y none la desactiva para ese elemento.

---
// src/layouts/Base.astro
---
<header transition:animate="slide">
  <slot name="titulo" />
</header>

<main transition:animate="fade">
  <slot />
</main>

<footer transition:animate="none">
  <slot name="pie" />
</footer>

Las animaciones aceptan además parámetros. En su forma de función puedes ajustar la duración o la curva de aceleración, escribiéndolo como una expresión de JavaScript en el atributo. Es útil para afinar el ritmo sin salir de las integradas.

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

transition:name: darle identidad a un elemento

transition:animate anima elementos en su sitio, pero no sabe que el título de la lista y el título del detalle son el mismo título. Para eso está transition:name: asigna a un elemento un nombre, que por debajo se traduce en la propiedad CSS view-transition-name. Cuando el navegador encuentra en la página vieja y en la nueva sendos elementos con el mismo nombre, no los funde por separado: entiende que uno es la continuación del otro y anima la transformación de su posición, tamaño y forma. El elemento morfa —viaja y se transforma— en lugar de aparecer y desaparecer.

Ese es el efecto que hace que una miniatura de una lista parezca despegarse y crecer hasta convertirse en la imagen de portada de la página de detalle. Basta con que ambos elementos, en sus páginas respectivas, compartan el mismo transition:name.

<!-- src/pages/blog/index.astro -->
<a href="/blog/astro-7">
  <img src="/astro-7.jpg" transition:name="portada-astro-7" />
  <h2 transition:name="titulo-astro-7">Astro 7 en 2026</h2>
</a>
<!-- src/pages/blog/astro-7.astro -->
<img src="/astro-7.jpg" transition:name="portada-astro-7" />
<h1 transition:name="titulo-astro-7">Astro 7 en 2026</h1>

En el salto entre esas dos páginas, la imagen y el título no se desvanecen: se desplazan y se redimensionan desde donde estaban en la lista hasta donde van en el detalle. El navegador hace el trabajo pesado de interpolar el movimiento; tú solo declaraste la identidad compartida.

💡
Identidad y animación son preguntas separadas

transition:name responde a quién es la continuación de quién; transition:animate responde a cómo se mueve. Puedes usarlas juntas o por separado. Un elemento con nombre pero sin animación explícita morfa con la transición por defecto; uno con animación pero sin nombre se anima en su sitio sin emparejarse con nada. La mayoría de los efectos memorables nacen de nombrar bien un puñado de elementos clave —la portada, el título, la barra de navegación— y dejar que el resto se funda.

La regla de unicidad, y por qué importa

Hay una restricción que no se puede quebrantar: en el instante de la transición, cada transition:name debe ser único dentro de cada página. Un nombre identifica un elemento, no una clase de elementos. Si dos elementos de la misma página comparten nombre, el navegador no sabe cuál emparejar y la transición se rompe o se ignora. Por eso los nombres literales fijos funcionan para elementos singulares —solo hay un encabezado, una portada, un pie— pero fallan en cuanto los repites.

El caso donde esto muerde es la lista. Si pintas veinte tarjetas y a todas les das transition:name="tarjeta", has violado la regla veinte veces. La solución es generar un nombre distinto para cada una a partir de un dato que ya las distingue: su identificador, su slug, su posición.

---
// src/pages/blog/index.astro
const posts = await getCollection('blog');
---
<ul>
  {posts.map((post) => (
    <li>
      <a href={`/blog/${post.id}`}>
        <img src={post.data.portada} transition:name={`portada-${post.id}`} />
        <h2 transition:name={`titulo-${post.id}`}>{post.data.title}</h2>
      </a>
    </li>
  ))}
</ul>

En la página de detalle, ese mismo post.id reconstruye el nombre y cierra el emparejamiento, de modo que solo la tarjeta en la que hiciste clic morfa hacia la portada, y no las otras diecinueve.

---
// src/pages/blog/[id].astro
const { post } = Astro.props;
---
<img src={post.data.portada} transition:name={`portada-${post.id}`} />
<h1 transition:name={`titulo-${post.id}`}>{post.data.title}</h1>
⚠️
Nombres duplicados: el error silencioso de las listas

Cuando una view transition entre una lista y un detalle no anima como esperabas —o directamente no anima—, sospecha antes que nada de nombres repetidos. Un transition:name fijo dentro de un map genera colisiones que el navegador resuelve ignorando el emparejamiento, casi siempre sin un error visible. La regla mental es tajante: si el elemento vive dentro de un bucle, su nombre debe derivar del dato que lo hace único. Nombre literal solo para lo que de verdad es singular en la página.

Componer un vocabulario de transiciones

Con estas dos directivas ya tienes un lenguaje completo. La estrategia que mejor escala no es animarlo todo, sino elegir los pocos elementos que dan continuidad visual entre dos vistas —la imagen que se agranda, el título que se mantiene, la cabecera que persiste— nombrarlos para que morfen, y dejar que el resto del contenido se funda con la transición por defecto. Menos nombres bien puestos comunican más que muchos nombres compitiendo. Un exceso de elementos morfando a la vez produce ruido; un puñado escogido produce la ilusión de que las dos páginas son dos estados de una misma superficie.

flowchart LR
A[lista miniatura name portada-astro-7] -->|mismo nombre morfa| C[detalle portada name portada-astro-7]
B[lista titulo name titulo-astro-7] -->|mismo nombre morfa| D[detalle titulo name titulo-astro-7]
style A fill:#89b4fa,color:#11111b
style B fill:#89b4fa,color:#11111b
style C fill:#a6e3a1,color:#11111b
style D fill:#a6e3a1,color:#11111b
🎞️

animate elige el como

fade funde, slide desliza segun la direccion, initial deja el default y none desactiva. Acepta parametros como duration.

🪪

name da identidad

Un mismo transition:name en dos paginas empareja los elementos y el navegador los morfa en vez de fundirlos.

⚠️

Un nombre, un elemento

Los nombres deben ser unicos por pagina en el instante de la transicion. Nada de nombres fijos dentro de un bucle.

🔑

Nombres desde el dato

En listas, deriva el nombre del id o slug para que solo la tarjeta pulsada morfe hacia su destino.

La animación no decora el cambio: cuenta que dos vistas son la misma cosa

Es tentador ver estas directivas como un adorno —un efecto bonito que se añade al final— pero eso confunde su función. Una view transition bien construida no embellece la navegación: la explica. Cuando una miniatura crece hasta ser la portada de la página siguiente, el movimiento comunica una proposición que ninguna palabra necesita decir: esto que ves ahora es aquello sobre lo que hiciste clic. La animación es, literalmente, un argumento de continuidad. Sin ella, dos páginas son dos hechos inconexos que el usuario debe volver a relacionar cada vez que salta; el destello de la recarga borra el contexto y obliga a reorientarse. Con transition:name, el elemento que persiste actúa de hilo: la mirada lo sigue, y al seguirlo entiende sin esfuerzo que no ha aterrizado en un lugar ajeno sino que ha profundizado en lo que ya miraba. Por eso la regla de unicidad no es un tecnicismo caprichoso, sino la condición misma de que la metáfora funcione: solo puede haber continuidad si el navegador sabe, sin ambigüedad, qué es la continuación de qué. Y por eso la mesura importa tanto: animar todo a la vez es afirmar que todo es continuo, lo cual equivale a no afirmar nada. El diseñador que domina estas dos directivas no está eligiendo efectos de un catálogo; está decidiendo qué relaciones entre vistas quiere volver visibles, y confiando en que el movimiento diga, mejor que cualquier texto, que el usuario nunca ha dejado de estar donde estaba.

⚔️ Haz que un elemento viaje entre páginas
  1. En una lista, dale a cada miniatura un transition:name derivado del id del elemento, y a cada título otro nombre igualmente único.
  2. En la página de detalle correspondiente, reconstruye esos mismos nombres a partir del id y comprueba que solo la tarjeta pulsada morfa hacia la portada.
  3. Aplica transition:animate="slide" a la cabecera y observa cómo el sentido del deslizamiento cambia al pulsar atrás.
  4. Provoca a propósito una colisión de nombres poniendo un transition:name fijo dentro del map, observa que la animación se rompe y luego corrígela.