Les SPA ont longtemps “simulé” la navigation : pushState à la main, popstate incomplet, scroll restauré au petit bonheur, transitions qui clignotent… Bref, ça marche, mais c’est fragile. La Navigation API arrive avec une promesse simple : revenir à une navigation fiable, standard, et pilotable — et elle est maintenant Baseline, donc utilisable sans vivre dans la peur du support navigateur.
Le vrai problème des routers SPA : ils reconstruisent un navigateur
Dans une SPA, on veut :
Intercepter un clic sur un lien interne (sans recharger)
Gérer back/forward proprement
Synchroniser URL, état, scroll, focus, analytics
Garder une UX fluide (chargements, transitions, pas de “flash”)
Historiquement, on fait ça avec :
history.pushState()/replaceState()window.onpopstate(qui n’est pas un événement de “navigation”, mais un signal de changement d’entrée d’historique)du code spécifique pour les formulaires, les redirects, les middlewares, la restauration du scroll…
Ça finit souvent en “framework routing” très intelligent… et très couplé au DOM, au runtime, et aux conventions internes. La Navigation API remet un cadre : un événement de navigation unifié, un modèle d’entrées d’historique modernisé, et des primitives pensées pour les soft navigations.
Sponsorisé par Le Scribouillard
Besoin de contenu optimisé SEO ?
Utilisez la meilleure plateforme française de création de contenu assistée par IA ! Et générez des articles pour moins de 1€ !
Navigation API : ce que ça change concrètement
Au lieu de guetter popstate et d’espérer ne rien casser, vous écoutez un vrai événement :
navigation+ événementnavigate: un point d’entrée unique pour gérer la navigationevent.intercept(): vous dites au navigateur “je prends la main” et vous exécutez une navigation SPA proprenavigation.navigate(): vous déclenchez une navigation comme un citoyen du navigateur (pas comme une bidouille)Entrées d’historique riches : vous manipulez des
NavigationHistoryEntry(id, key, index, state…)Scroll restoration et transitions : des hooks plus cohérents, et une meilleure intégration avec les transitions modernes
Adopter la Navigation API dans une app existante (sans tout réécrire)
Bonne nouvelle : vous pouvez l’introduire progressivement. L’objectif n’est pas de jeter votre router, mais de remplacer la plomberie (interception + historique + scroll) par l’API standard.
1) Mettre un feature flag (obligatoire en prod)
Commencez par détecter le support. Si ce n’est pas dispo, vous retombez sur votre mécanisme actuel (router classique).
2) Intercepter les navigations internes
Le cœur, c’est l’événement navigate. Vous filtrez ce que vous prenez en charge (même origine, pas de téléchargement, pas de navigation que vous ne savez pas gérer), puis vous interceptez.
// Router SPA minimal basé sur la Navigation API
function enableNavigationAPI({ render, load }) {
if (!('navigation' in window)) return false;
navigation.addEventListener('navigate', (event) => {
// URL cible
const url = new URL(event.destination.url);
// On ne gère que la même origine
if (url.origin !== location.origin) return;
// Si le navigateur demande explicitement un reload, on laisse faire
if (event.navigationType === 'reload') return;
// À vous de décider : on ignore certains cas (ex: téléchargement)
// if (event.downloadRequest) return;
event.intercept({
// Optionnel : contrôler le scroll (voir section dédiée)
scroll: 'manual',
async handler() {
// 1) Charger les données
const data = await load(url);
// 2) Rendre l'écran
render(url, data);
}
});
});
return true;
}Ce pattern a deux gros avantages :
Back/forward déclenche le même pipeline que vos navigations “clic”
Vous centralisez enfin la logique de navigation (et donc les effets de bord)
3) Déclencher une navigation “propre” depuis votre code
Dans beaucoup de SPA, on fait history.pushState puis on render. Ici, on peut naviguer via l’API :
navigation.navigate('/produits/42')navigation.navigate(url, { state: {...} })pour stocker un état d’entrée
Intérêt : vous restez dans le modèle “navigation” du navigateur, ce qui rend l’intégration plus cohérente (observabilité, transitions, comportements attendus).
State, historique, et “soft navigations” : enfin quelque chose de fiable
Le vieux couple pushState / popstate force souvent à réinventer :
une identité d’entrée d’historique
un mapping URL → écran
une gestion d’état (ex: quel onglet était ouvert sur la page précédente ?)
Avec la Navigation API, vous pouvez vous appuyer sur :
navigation.currentEntry: l’entrée courantenavigation.entries(): la liste des entrées (utile pour debug / UX avancée)entry.getState(): récupérer l’état associé
Le résultat : vous pouvez stocker des infos utiles (un filtre, un tri, une ancre logique) sans polluer l’URL, tout en gardant un back/forward cohérent.
Scroll restoration : arrêter de casser le cerveau de l’utilisateur
La restauration du scroll est un des sujets les plus pénibles en SPA :
Back doit revenir au scroll précédent (comme un vrai site)
Une navigation “nouvelle page” doit souvent remonter en haut
Mais certaines navigations internes (tabs, filters) ne doivent pas bouger
Avec event.intercept({ scroll: 'manual' }) vous reprenez la main de façon explicite, au bon endroit (dans le handler de navigation), au lieu d’éparpiller ça dans 12 hooks.
navigation.addEventListener('navigate', (event) => {
const url = new URL(event.destination.url);
if (url.origin !== location.origin) return;
event.intercept({
scroll: 'manual',
async handler() {
await appNavigate(url);
// Exemple de règle simple :
// - back/forward : restaurer
// - navigation classique : top
if (event.navigationType === 'traverse') {
// Si votre app stocke des positions, restaurez ici.
restoreScrollForEntry(event.destination);
} else {
window.scrollTo({ top: 0, left: 0, behavior: 'instant' });
}
}
});
});À adapter à votre UI (listes, pages infinies, scroll dans un container, etc.), mais l’idée est là : la navigation devient le chef d’orchestre.
Perf et UX : là où ça fait vraiment la différence
La Navigation API n’est pas “juste un nouvel event”. Elle aide à mettre au propre des pratiques qui impactent directement vos métriques et la sensation de qualité.
Moins de jank, transitions plus propres
Quand la navigation est centralisée, vous pouvez :
déclencher un état “loading” cohérent (pas 3 loaders concurrents)
couper les requêtes précédentes quand une nouvelle navigation arrive (abort)
brancher des transitions (y compris des transitions de vue) sans hacks
Soft navigations mieux instrumentées
Le web moderne mesure de plus en plus les “soft navigations” (navigations sans rechargement) côté navigateurs et outils. Une navigation SPA qui suit un modèle standard est plus simple à observer, et plus facile à rendre robuste (notamment quand votre app grossit).
Cas limites à anticiper (sinon vous allez vous tirer une balle dans le pied)
Liens externes et cross-origin : ne les interceptez pas.
Téléchargements : laissez le navigateur gérer.
Reload explicite : si l’utilisateur force un refresh, ne luttez pas.
Form submissions : choisissez une stratégie claire (vraie soumission vs interception SPA). La Navigation API est justement un endroit propre pour traiter ce cas, au lieu d’un patchwork d’handlers.
Accessibilité : après navigation, gérez focus et titre de page. Une navigation propre n’est pas une navigation accessible par magie.
Une stratégie réaliste : “Navigation API d’abord, router ensuite”
Si vous avez déjà React Router, Vue Router, Nuxt, etc., l’approche la plus saine est :
Introduire la Navigation API comme couche de navigation bas niveau (interception/historique)
Faire passer votre router applicatif (matching routes → rendu) derrière
event.intercept()Déplacer progressivement scroll restoration, analytics, gestion des annulations dans ce pipeline unique
Vous gardez vos routes, vos loaders, vos layouts… mais vous arrêtez de bricoler le navigateur.
Conclusion
Le meilleur argument pour la Navigation API n’est pas “c’est nouveau”. C’est : ça simplifie un problème que les SPA ont rendu inutilement compliqué. Interception standard, historique plus fiable, scroll maîtrisé, transitions mieux intégrées… et maintenant que c’est Baseline, on peut enfin la considérer comme un choix pragmatique, pas un pari.