Navigation API

A modern replacement for the History API — intercept, manage, and navigate between SPA “pages” with a single event listener. Replaces pushState/popstate/hashchange boilerplate entirely.

1. SPA Navigation — single navigate event handler

Click the nav tabs to switch “pages”. Each click uses navigation.navigate() to push a URL, and a single navigate event handler switches the content panel. Check the browser URL bar — it updates without page reload.

Home

Welcome to the SPA home page. Navigation is handled by a single navigate event listener — no router library needed.

About

This is the about page. The browser back/forward buttons work because the Navigation API manages history entries natively.

Contact

Contact page content. Each navigation creates a proper history entry with state — all via navigation.navigate().

2. History State — navigation.updateCurrentEntry({ state })

Type in the form fields. State is saved to the current navigation entry via updateCurrentEntry. Navigate away and come back — the state is restored from currentEntry.getState().

3. Back/Forward Controls — canGoBack / canGoForward

Native navigation controls using navigation.back(), navigation.forward(), and the canGoBack/canGoForward properties. Buttons auto-disable when navigation is not possible.

Navigate between pages above to build history entries.

4. Code Comparison — History API vs Navigation API

History API
// Push state
history.pushState(state, '', url);

// Listen for back/forward
window.addEventListener('popstate', e => {
  renderPage(e.state);
});

// Intercept link clicks
document.addEventListener('click', e => {
  const link = e.target.closest('a');
  if (!link) return;
  if (link.origin !== location.origin)
    return;
  e.preventDefault();
  history.pushState(
    getState(link),
    '',
    link.href
  );
  renderPage(getState(link));
});

// No canGoBack/canGoForward
// No navigation entries list
// No intercept() for async
// hashchange needed separately
window.addEventListener(
  'hashchange', handleHash
);
Navigation API
// One handler for everything
navigation.addEventListener(
  'navigate', e => {
    if (!e.canIntercept) return;
    const url = new URL(e.destination.url);
    e.intercept({
      handler() {
        renderPage(url);
      }
    });
  }
);

// Built-in back/forward
navigation.back();
navigation.forward();

// Built-in state
navigation.canGoBack;
navigation.canGoForward;
navigation.currentEntry.getState();
navigation.entries();

// That's it. No hashchange.
// No link click interception.
// No popstate. One event.
Implementation: Navigation API (key code)
<script>
// Feature detection
if (!window.navigation) {
  alert('Navigation API not supported');
}

// Single navigate event handler
navigation.addEventListener('navigate', e => {
  if (!e.canIntercept) return;
  if (!e.destination.sameDocument) return;

  const url = new URL(e.destination.url);
  e.intercept({
    handler() {
      // Switch page content based on URL
      showPage(url.hash.slice(2) || 'home');
    }
  });
});

// Programmatic navigation with state
navigation.navigate('#/about', {
  state: { from: 'home' }
});

// Read state from current entry
const state = navigation.currentEntry.getState();

// Update state without navigation
navigation.updateCurrentEntry({
  state: { ...state, newField: 'value' }
});
</script>