Astro y transition:persist: el enlace que se quedó congelado
Astro permite que un sitio estático se navegue sin recargas completas
gracias a las View Transitions. Con la directiva
transition:persist, un elemento como la barra superior se
conserva entre páginas en vez de volver a dibujarse, lo que evita
parpadeos y mantiene su estado. Es muy útil y tiene una consecuencia que
no es evidente hasta que rompe algo.
El bug
El logo de este sitio enlaza a distintos destinos según la página. En la
portada apunta al ancla #inicio, para volver arriba sin
recargar. En cualquier otra página apunta a /. El valor se
pasaba como prop al componente de la barra, que tenía
transition:persist.
Al entrar por la portada y navegar a la página de términos, el logo
seguía enlazando a #inicio. Resuelto contra la URL actual,
eso llevaba a /terminos/#inicio, un ancla que no existe en
esa página. Hacer clic en el logo no hacía nada.
Por qué pasa
Un elemento persistido no se vuelve a renderizar al navegar: se toma el nodo del DOM de la página anterior y se inserta tal cual en la nueva. Por lo tanto, todo lo que se calculó en el servidor para ese elemento queda congelado con los valores de la primera página donde se renderizó. Los props de la página nueva existen en su HTML, pero se descartan en favor del nodo persistido.
El mismo problema ya había aparecido antes con el estado visual: la sección activa de una barra lateral persistida quedaba marcada según la primera página visitada. Esta vez afectaba a contenido, un enlace, y por eso rompía la navegación.
La corrección
Todo valor de un elemento persistido que dependa de la página actual se
recalcula en el navegador después de cada navegación. Astro emite el
evento astro:page-load al terminar de cargar cada página,
incluida la primera. En ese evento, el script lee la ruta actual y ajusta
el enlace del logo y la marca de sección activa.
La regla práctica: nunca confiar en un valor renderizado en el servidor para un elemento que persiste entre navegaciones, si ese valor cambia de una página a otra.
Una trampa vecina: CSS con ámbito
Otra sorpresa de Astro, que ya causó el mismo bug dos veces en proyectos distintos. Los estilos escritos en un componente se limitan a ese componente agregando un atributo único a su marcado. Ese atributo solo se agrega a los elementos que el componente renderiza al compilar.
Si un script crea elementos después, por ejemplo al insertar la lista de archivos cargados, esos elementos no tienen el atributo y los estilos del componente no los alcanzan. El fallo es silencioso: el elemento cae al estilo por defecto del navegador y se ve raro, pero nada se rompe. Para contenido generado por JavaScript, las reglas deben declararse como globales, idealmente acotadas bajo un contenedor propio para no afectar al resto del sitio.
Lista de verificación
- Revisar cada elemento con
transition:persisty listar los props que dependen de la página. - Recalcular esos valores en
astro:page-load. - Probar navegando entre páginas distintas, no solo recargando cada una.
- En componentes que insertan HTML por JavaScript, verificar que sus estilos sean globales o usen el selector global.