Navigation API: historial web sin los hacks de la History API

La History API lleva con nosotros desde HTML5. Con history.pushState() puedes cambiar la URL sin recargar la página, que fue un avance enorme en su momento. El problema es que se quedó a medias.

Cuando el usuario pulsa el botón atrás del navegador, la History API no te avisa de nada útil. Puedes escuchar el evento popstate, pero no te dice si el usuario va hacia atrás o hacia delante. Tampoco puedes interceptar navegaciones que el navegador inicia por su cuenta. Y si quieres cancelar una navegación en mitad del proceso, suerte.

Los frameworks de SPA llevan años parchando esto. React Router, Vue Router y compañía hacen malabarismos con eventos, proxies y hacks varios para simular un control de navegación que el navegador directamente no exponía. Eso cambia ahora.

Qué es la Navigation API

La Navigation API añade window.navigation, un objeto que da control real sobre la navegación del documento. La diferencia clave con la History API es la cobertura: intercepta absolutamente todas las navegaciones del documento, no solo las que tú disparas desde el código.

Eso incluye clicks en <a>, llamadas a history.pushState() desde cualquier parte del código, navegaciones con location.assign() y el botón atrás o adelante del navegador. Todo.

Estaba disponible en Chrome desde la versión 102. A partir de 2026 es ya Baseline, lo que significa que Firefox y Safari modernos también la soportan. Puedes usarla sin polyfills en producción.

El evento navigate

El corazón de la API es el evento navigate. Se dispara antes de que ocurra cualquier navegación y te da la oportunidad de decidir qué hacer:

navigation.addEventListener('navigate', (event) => {
  if (!event.canIntercept) return; // navegación cross-origin, déjala pasar

  event.intercept({
    handler: async () => {
      // aquí cargas datos, actualizas la UI, lo que necesites
      const data = await fetchPageData(event.destination.url);
      renderPage(data);
    }
  });
});

event.canIntercept te dice si puedes tomar el control. Las navegaciones cross-origin siempre devuelven false: tiene sentido, no deberías poder interceptar que el usuario vaya a otro dominio. Cuando canIntercept es true, llamas a event.intercept() y le pasas un handler asíncrono. La navegación se considera completada cuando ese handler termina. Si no llamas a intercept(), la navegación ocurre con normalidad.

Detectar si el usuario va hacia delante o hacia atrás

Aquí es donde la Navigation API marca una diferencia clara respecto a lo anterior. El evento navigate incluye la propiedad navigationType, que puede ser 'push', 'replace', 'reload' o 'traverse'.

'traverse' indica que el usuario está moviéndose por el historial, ya sea hacia atrás o hacia delante. Para saber la dirección exacta, compara los índices:

navigation.addEventListener('navigate', (event) => {
  if (event.navigationType === 'traverse') {
    const goingBack = event.destination.index < navigation.currentEntry.index;
    console.log(goingBack ? 'El usuario va hacia atrás' : 'El usuario va hacia delante');
  }
});

Con la History API, conseguir esto requería llevar un registro manual de las URLs visitadas. Aquí lo tienes gratis.

navigation.navigate(): sin más pushState

Para navegar desde el código ya no necesitas history.pushState(). La Navigation API tiene su propio método:

const result = navigation.navigate('/articulos/nuevo', {
  state: { scrollPosition: 0, filtros: ['javascript', 'web'] }
});

// navigate() devuelve una promesa
await result.finished;
console.log('Navegación completada');

El estado que pasas se guarda en el historial y está disponible cuando el usuario vuelve a esa entrada. Para recuperarlo:

const estado = navigation.currentEntry.getState();
console.log(estado.filtros); // ['javascript', 'web']

Esto es especialmente útil para guardar la posición de scroll o el estado de filtros antes de que el usuario entre en el detalle de algo y luego quiera volver.

Transiciones de vista con View Transitions API

La Navigation API encaja bien con la View Transitions API, otra incorporación reciente al navegador. Juntas permiten animar el cambio entre páginas sin un framework:

navigation.addEventListener('navigate', (event) => {
  if (!event.canIntercept) return;

  event.intercept({
    handler: async () => {
      const data = await fetchPageData(event.destination.url);

      // el navegador captura el estado actual, aplica los cambios
      // y anima la diferencia automáticamente
      document.startViewTransition(() => {
        renderPage(data);
      });
    }
  });
});

El navegador toma una captura del estado antes del cambio y otra después, y genera la animación entre ambas. Puedes personalizar la transición con CSS usando los pseudo-elementos ::view-transition-old y ::view-transition-new. Sin librerías de animación, sin configuración complicada.

Acceder al historial de navegación

La Navigation API también expone el historial completo del documento:

// todas las entradas del historial del documento actual
const entradas = navigation.entries();
entradas.forEach(entry => {
  console.log(entry.url, entry.index);
});

// entrada actual
console.log(navigation.currentEntry.url);

// navegar programáticamente por el historial
await navigation.go(-2).finished; // dos páginas atrás

A diferencia de history.go(), aquí puedes esperar a que la navegación termine y reaccionar en consecuencia. Y si has registrado un handler en el evento navigate, también se ejecuta en estas navegaciones programáticas.

¿Cuándo usarla directamente y cuándo dejar que lo haga el framework?

Depende del proyecto. Si estás construyendo una SPA propia sin framework, una PWA o cualquier proyecto donde quieres evitar dependencias de routing, la Navigation API te da todo lo que necesitas de serie.

Si usas TypeScript con React Router 7+ o TanStack Router, ya la usan internamente. No necesitas tocarla directamente, aunque entender cómo funciona te ayuda a debugar comportamientos raros de routing.

Con Vue Router o Next.js la situación es similar: el framework lo gestiona por ti. Pero si alguna vez necesitas hacer algo que el framework no cubre, saber que puedes acceder a window.navigation directamente es útil.

En definitiva, la Navigation API no inventa nada nuevo conceptualmente. Lo que hace es exponer de forma limpia y completa algo que los navegadores ya hacían internamente, y que hasta ahora solo podías controlar de manera parcial y con muchos rodeos.

Imagen: Pexels / Rashed Paykary

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP