惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cloudbric
Cloudbric
T
Threat Research - Cisco Blogs
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog
P
Privacy & Cybersecurity Law Blog
H
Help Net Security
云风的 BLOG
云风的 BLOG
G
GRAHAM CLULEY
Spread Privacy
Spread Privacy
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
Arctic Wolf
Project Zero
Project Zero
Engineering at Meta
Engineering at Meta
P
Privacy International News Feed
Blog — PlanetScale
Blog — PlanetScale
Stack Overflow Blog
Stack Overflow Blog
M
MIT News - Artificial intelligence
The Register - Security
The Register - Security
Recorded Future
Recorded Future
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cisco Blogs
PCI Perspectives
PCI Perspectives
Recent Announcements
Recent Announcements
Martin Fowler
Martin Fowler
A
About on SuperTechFans
W
WeLiveSecurity
GbyAI
GbyAI
V
Vulnerabilities – Threatpost
The GitHub Blog
The GitHub Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Check Point Blog
Y
Y Combinator Blog
月光博客
月光博客
Scott Helme
Scott Helme
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News
F
Fortinet All Blogs
U
Unit 42
G
Google Developers Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Threatpost
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google Online Security Blog
Google Online Security Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Cisco Talos Blog
Cisco Talos Blog
博客园 - 三生石上(FineUI控件)
Hugging Face - Blog
Hugging Face - Blog
MongoDB | Blog
MongoDB | Blog
博客园 - 司徒正美

web.dev: Blog

New to the web platform in May  |  Blog  |  web.dev April 2026 Baseline monthly digest  |  Blog  |  web.dev New to the web platform in April  |  Blog  |  web.dev March 2026 Baseline monthly digest  |  Blog  |  web.dev February 2026 Baseline monthly digest  |  Blog  |  web.dev New to the web platform in March  |  Blog  |  web.dev January 2026 Baseline monthly digest  |  Blog  |  web.dev New to the web platform in February  |  Blog  |  web.dev Interop 2026: Continuing to improve the web for developers  |  Blog  |  web.dev
Navigation API - a better way to navigate, is now Baseline Newly Available  |  Blog  |  web.dev
2026-02-17 · via web.dev: Blog

Navigation API - a better way to navigate, is now Baseline Newly Available

Jay Rungta

Published: Feb 17, 2026

For over a decade, we've relied on window.history to build Single Page Applications (SPAs), and for over a decade, we've complained about it. The reason? It was never really designed for SPAs. To provide a seamless experience, SPAs must mimic standard multi-page navigation by manually tapping into the browser's history to support the back and forward buttons.

The History API alleviated some of these pain points, but not without its own shortcomings, like being unable to detect all different types of navigation triggers. Other notable limitations include the inability to read the full history stack, or edit non-current entries. The popstate event also behaves inconsistently, and doesn't trigger when pushState or replaceState are called programmatically.

That era ends now. The Navigation API is here, and it's finally becoming Baseline Newly available across all major browsers as of early 2026.

A side by side comparison

To illustrate the difference, this section compares how we've historically handled client-side routing versus the new, streamlined approach powered by the Navigation API.

The old way

// 1. Function to navigate programmatically
function navigate(path) {
  // Update the URL without a page refresh
  window.history.pushState({ path }, '', path);

  // Manually trigger the UI update
  renderContent(path);
}

// 2. Listener for browser navigation (Back/Forward buttons)
window.addEventListener('popstate', (event) => {
  // event.state contains the object we pushed in navigate()
  const path = event.state?.path || window.location.pathname;
  renderContent(path);
});

// 3. Mock UI renderer
function renderContent(path) {
  console.log(`Rendering UI for: ${path}...`);
}

// Example usage:
// navigate('/dashboard');

How the Navigation API works

// 1. One central listener for ALL navigation
// This catches: links, back/forward buttons, AND programmatic calls
navigation.addEventListener('navigate', (event) => {
  const url = new URL(event.destination.url);

  // Intercept the navigation to prevent a full page reload
  event.intercept({
    async handler() {
      // The API handles the URL update; you just handle the UI
      await renderContent(url.pathname);
    }
  });
});

// 2. Mock UI renderer
async function renderContent(path) {
    console.log(`Rendering UI for: ${path}...`);
}

// Example usage:
// navigation.navigate('/dashboard');

Building a router with the History API felt like assembling a puzzle, since you had to:

  1. Listen for clicks on <a> tags globally.
  2. Call preventDefault() on them.
  3. Manually call history.pushState().
  4. Manually update your DOM.
  5. Separately listen for the popstate event to handle the back/forward buttons.

If you forgot to handle one edge case, users might accidentally end up at the wrong view, highlighting its fragility.

The Navigation API radically simplifies this. It gives you a single, centralized NavigateEvent for every navigation—whether it's a user clicking a link, submitting a form, hitting the back button, or your code calling navigation.navigate().

The event.intercept() function does a lot of the heavy lifting for you:

  • Automatic URL updates: Handles updating the address bar and the history stack, without the need to call pushState.
  • Automatic accessibility: Handles accessibility primitives like focus management (restoring focus after navigation) automatically.
  • Centralized logic: Handles the back button and click events in the exact same function.

Some more use cases

We look at a few more examples in this section to highlight more ways the Navigation API can be leveraged.

Example: Handling a form submission

The navigate event automatically catches all same-document form submissions and provides a NavigateEvent.formData property to access the data.

This example captures a standard HTML form submission, prevents the page reload, and processes the data asynchronously.

// 1. One central listener handles links AND forms
navigation.addEventListener('navigate', (event) => {
  // Only handle form POST submissions in this block
  if (event.formData && event.canIntercept) {
    event.intercept({
      async handler() {
        const data = event.formData;
        console.log(`Submitting form data...`);

        const username = data.get('username');

        // Perform your async API call
        postFormData(data);

        // Update UI without a page refresh
        renderSuccessMessage(username);
      }
    });
  }
});

// 2. Standard HTML form (No JS 'onsubmit' needed!)
// <form action="/login" method="POST">
//   <input name="username" type="text" required />
//   <button type="submit">Login</button>
// </form>

Example: Handling async scrolling

In the Navigation API, event.scroll() gives you manual control over when the browser restores scroll position during a navigation.

By default, the browser tries to restore scroll position as soon as event.intercept() is called. However, in modern SPAs, the content often isn't ready yet (you might be waiting for an API response). If the browser scrolls before the content is rendered, it will land in the wrong place or stay at the top.

Imagine a user clicks the back button to return to a long list of items. We will need to fetch those items before the page is long enough to go to the end.

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

  event.intercept({
    // Tells the browser: "I will handle the scroll timing manually"
    scroll: 'manual',

    async handler() {
      // 1. Fetch data and render it
      const data = await fetchListData();
      renderItems(data);

      // 2. Now that the items are in the DOM and the page has height,
      //    we can move the scrollbar to the saved position (for back/forward)
      //    or to the top (for new navigations).
      event.scroll();
    }
  });
});

Example: Enabling view transitions

The Navigation API and View Transitions API are designed to work together to create seamless, "app-like" transitions in SPAs.

While intercepting a navigation event, we can wrap the DOM updates in a document.startViewTransition(). This tells the browser to capture a snapshot of the "old" state, perform the required DOM changes, and then animate to the "new" state. This can allow us to support app-like transitions!

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

  const url = new URL(event.destination.url);

  event.intercept({
    async handler() {
      // 1. Fetch the new content first (optional but recommended)
      const content = await fetchNewPageContent(url.pathname);

      // 2. Start the view transition
      document.startViewTransition(() => {
        // 3. Update the DOM inside the callback
        // The browser snapshots the old UI before this and the new UI after
        document.getElementById('app').innerHTML = content;
      });
    }
  });
});

In summary

As seen with the preceding examples, the Navigation API fixes deep architectural issues with navigating SPAs, a long-standing pain point among many web developers. It is built-in, safe, and handles edge cases robustly.

As of early 2026, with support landing in Safari and Firefox, the Navigation API is ready for prime time. It's the router we always wanted—simple, powerful, and built for the modern web.

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2026-02-17 UTC.

[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2026-02-17 UTC."],[],[]]