note

Réhydrater le Javascript avec ClientRouter d’Astrojs

3 novembre 2025 lrtrln Decrypt
Réhydrater le Javascript avec ClientRouter d’Astrojs

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 removeEventListener avant addEventListener si 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

EventQuand l’utiliser
astro:page-loadRéinitialiser le JS après navigation (99% des cas)
astro:before-preparationAvant de charger la nouvelle page (ex: fermer un menu)
astro:after-swapJuste 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)