Navigation API
Un solo evento para todas las navegaciones: el router estándar que sustituye a pushState y popstate.
navigation.addEventListener('navigate') recibe enlaces, formularios, atrás y adelante, recargas y cambios de location. Con event.intercept() la página decide qué pintar sin recargar, y el navegador lleva el historial, el foco y el scroll.
Es Baseline desde el 13 de enero de 2026 (Firefox 147, con Safari 26.2 un mes antes). precommitHandler, para redirigir o cancelar antes de que cambie la URL, está en Chrome 141 y Firefox 147, y forma parte de Interop 2026.
Para qué sirve
- Routers de aplicaciones de una sola página sin librerías
- Interceptar navegaciones para guardar cambios pendientes
- Saber de verdad qué hay en el historial (navigation.entries())
Demo
Una mini app dentro de esta página: los enlaces cambian la URL (?vista=…) sin recargar, y atrás y adelante del navegador también funcionan. «Zona privada» redirige antes de cambiar la URL (precommitHandler).
Compatibilidad
Baseline · nueva · 13 ene 2026| Función | Escritorio | Móvil | |||||
|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Safari | Chrome Android | Firefox Android | Safari iOS | |
| Navigation (window.navigation) | 102Chrome: 102 | 102Edge: 102 | 147Firefox: 147 | 26.2Safari: 26.2 | 102Chrome: 102 | 147Firefox: 147 | 26.2Safari: 26.2 |
| intercept({ precommitHandler }) | 141Chrome: 141 | 141Edge: 141 | 147Firefox: 147 | —Safari: No | 141Chrome: 141 | 147Firefox: 147 | —Safari: No |
Compatibilidad generada en cada publicación desde MDN browser-compat-data 8.1.3 (24 sep 2026) · web-features 3.40.0 · revisión manual: 1 oct 2026
Código mínimo
<!doctype html>
<meta charset="utf-8">
<title>Navigation API · ejemplo mínimo</title>
<!-- Un enrutador de una sola página con la Navigation API. Sírvelo por HTTP (npx serve .), no con file://. -->
<nav>
<a href="?vista=inicio">Inicio</a> ·
<a href="?vista=obra">Obra</a> ·
<a href="?vista=contacto">Contacto</a> ·
<button id="back">Atrás</button>
</nav>
<main id="view"></main>
<script type="module">
const view = document.querySelector('#view');
const pages = { inicio: 'Bienvenida', obra: 'Trabajos', contacto: 'Escríbeme' };
const render = (url) => {
const vista = new URL(url).searchParams.get('vista') ?? 'inicio';
view.textContent = pages[vista] ?? 'No encontrada';
};
if (!('navigation' in window)) {
view.textContent = 'Sin Navigation API: los enlaces recargan la página.';
} else {
render(location.href);
// Un único punto para todas las navegaciones: enlaces, formularios, atrás/adelante, location…
navigation.addEventListener('navigate', (event) => {
if (!event.canIntercept || event.hashChange || event.downloadRequest) return;
const url = new URL(event.destination.url);
if (url.pathname !== location.pathname) return; // deja pasar lo que no es de esta página
event.intercept({
async handler() {
await new Promise((r) => setTimeout(r, 150)); // aquí iría un fetch
render(url);
},
});
});
document.querySelector('#back').onclick = () => navigation.canGoBack && navigation.back();
navigation.addEventListener('navigatesuccess', () => console.log('Entradas:', navigation.entries().length));
}
</script><!doctype html>
<meta charset="utf-8">
<title>Navigation API · ejemplo mínimo</title>
<!-- Un enrutador de una sola página con la Navigation API. Sírvelo por HTTP (npx serve .), no con file://. -->
<nav>
<a href="?vista=inicio">Inicio</a> ·
<a href="?vista=obra">Obra</a> ·
<a href="?vista=contacto">Contacto</a> ·
<button id="back">Atrás</button>
</nav>
<main id="view"></main>
<script type="module">
const view = document.querySelector('#view');
const pages = { inicio: 'Bienvenida', obra: 'Trabajos', contacto: 'Escríbeme' };
const render = (url) => {
const vista = new URL(url).searchParams.get('vista') ?? 'inicio';
view.textContent = pages[vista] ?? 'No encontrada';
};
if (!('navigation' in window)) {
view.textContent = 'Sin Navigation API: los enlaces recargan la página.';
} else {
render(location.href);
// Un único punto para todas las navegaciones: enlaces, formularios, atrás/adelante, location…
navigation.addEventListener('navigate', (event) => {
if (!event.canIntercept || event.hashChange || event.downloadRequest) return;
const url = new URL(event.destination.url);
if (url.pathname !== location.pathname) return; // deja pasar lo que no es de esta página
event.intercept({
async handler() {
await new Promise((r) => setTimeout(r, 150)); // aquí iría un fetch
render(url);
},
});
});
document.querySelector('#back').onclick = () => navigation.canGoBack && navigation.back();
navigation.addEventListener('navigatesuccess', () => console.log('Entradas:', navigation.entries().length));
}
</script>Novedades
Ten en cuenta
- Comprueba canIntercept: las navegaciones a otro origen no se pueden interceptar.
- Pasar precommitHandler en un evento no cancelable lanza un SecurityError.
- Deja pasar las descargas y los cambios de ancla (downloadRequest, hashChange).
Más información
Fuentes
- web-features 3.40.0 · navigation (Baseline 2026-01-13)30 sep 2026
- MDN · NavigateEvent.intercept()27 ago 2026
- Chrome 146 release notes10 mar 2026
- Interop 202612 feb 2026