Le ClientRouter d’Astro introduit une navigation fluide et instantanée entre les pages. Il change toutefois la façon dont les scripts sont exécutés : le JavaScript n’est plus relancé à chaque chargement, ce qui demande un léger ajustement côté code.
Contexte : l’arrivée du <ClientRouter /> dans Astro
Introduit avec Astro 4.5, le composant <ClientRouter /> fait entrer Astro dans une logique de navigation fluide côté client, sans rechargement complet du navigateur.
L’idée : offrir une expérience “app” (transitions instantanées, état préservé, animations possibles) tout en gardant la philosophie Astro : générer du HTML statique par défaut, puis hydrater seulement les parties nécessaires.
Concrètement, <ClientRouter /> intercepte les clics sur les liens internes (<a href="/...">) et met à jour la section ciblée du DOM (souvent <main>) via morphdom, sans déclencher de nouveau chargement serveur. Résultat : navigation plus rapide, pas de flash visuel, pas de rechargement global du JS ou du CSS.
Mais cette fluidité peut engendrer des revers : les scripts inline ou les comportements JavaScript attachés au DOM initial ne sont plus relancés après les transitions. Ce qui marchait parfaitement dans un site Astro classique (scroll animations, menu mobile, accordéon, etc.) se retrouve soudain “gelé” après la première navigation.
Le problème
Avec <ClientRouter />, Astro garde le layout global (header, footer, scripts statiques) en mémoire et ne remplace que le contenu de la page. C’est ce qui rend la navigation si rapide, mais c’est aussi ce qui piège les scripts écrits un peu “à l’ancienne”.
Un code inline placé dans un composant .astro ou en bas du <body> s’exécute une fois au chargement initial, puis… plus jamais. Le DOM change, mais le script ne voit rien : il ne se réattache pas, ne met plus à jour ses états. D’où les menus figés, les animations inertes, et les comportements “morts” après la première transition
Donc avec <ClientRouter /> activé, les scripts inline Astro ne sont exécutés qu’une seule fois au chargement initial. Après une navigation côté client, le DOM change mais les scripts ne sont pas réexécutés.
La solution
1. Structure de base
function initMonComposant() {
const element = document.querySelector('.mon-element')
if (!element) return // ⚠️ Toujours vérifier si l'élément existe
// logic...
}
// Initialisation au chargement
initMonComposant()
// Réinitialisation après chaque navigation
document.addEventListener('astro:page-load', () => {
initMonComposant()
})
2. Points importants
- Retirer
is:inline- Pas nécessaire, utilise<script>normal - Encapsuler dans une fonction - Pour pouvoir réexécuter le code
- Toujours vérifier l’existence des éléments -
if (!element) return - Utiliser
astro:page-load- Event déclenché après chaque navigation (même la première) - Éviter les event listeners multiples - Utiliser
removeEventListeneravantaddEventListenersi nécessaire
3. Exemple avec cleanup des event listeners
function initMonComposant() {
const button = document.querySelector('.mon-button')
if (!button) return
const handleClick = () => {
console.log('clicked!')
}
// Nettoyer avant d'ajouter (évite les doublons)
button.removeEventListener('click', handleClick)
button.addEventListener('click', handleClick)
}
initMonComposant()
document.addEventListener('astro:page-load', initMonComposant)
4. Events Astro utiles
| Event | Quand l’utiliser |
|---|---|
astro:page-load | Réinitialiser le JS après navigation (99% des cas) |
astro:before-preparation | Avant de charger la nouvelle page (ex: fermer un menu) |
astro:after-swap | Juste après le swap du DOM |
5. Fermer un menu lors de la navigation
document.addEventListener('astro:before-preparation', () => {
const menu = document.querySelector('.mobile-menu')
if (menu?.classList.contains('show')) {
menu.classList.remove('show')
}
})
</>
En bref
Pour chaque composant avec du JS :
- Encapsuler le code dans une fonction
initMonComposant() - Appelle direct de la fonction
- Puis appel avec
astro:page-load - Toujours vérifier que tes éléments existent avec
if (!element) return
Pattern magique :
function init() { /* code */ }
init()
document.addEventListener('astro:page-load', init)