Navigation API
Published Updated
In one line: per MDN, “the Navigation API provides the ability to initiate,
intercept, and manage browser navigation actions,” as a successor to the History API
and window.location aimed at the needs of single-page applications (SPAs).
What it is
Section titled “What it is”The API is accessed via Window.navigation, which per MDN “returns a reference to a
global Navigation object” — each window has its own instance. Its most notable
event is navigate, which per MDN’s Navigation API overview “is fired when any type
of navigation is initiated, meaning that you can control all page navigations from
one central place, ideal for routing functionality in SPA frameworks.” MDN’s
navigate_event reference describes the same event more narrowly: it “fires when any
type of navigation is initiated, allowing you to intercept as required.”
Where it is supported
Section titled “Where it is supported”Per MDN’s browser-compat-data, the Navigation interface is supported in Chrome from
version 102 (Chrome for Android from 102), in Firefox from version 147, and in Safari
from version 26.2 (Safari on iOS from 26.2) — the compat data marks Edge, Opera, and
Samsung Internet entries as "mirror", meaning their support data is derived from the
corresponding upstream Chromium release rather than stated directly; it is not a claim
that those browsers share Chrome’s own version numbers. The NavigateEvent interface itself shipped alongside it, also
from Chrome 102; only its current intercept() method arrived slightly later, at
Chrome 105 (an earlier, differently-named method, transitionWhile, existed from
Chrome 102 until it was removed at Chrome 108). Firefox 147 and Safari 26.2 shipped
NavigateEvent and intercept() together.
How to use it
Section titled “How to use it”Listen for the navigate event and call intercept() to handle the navigation with
custom logic instead of a full page load. This example assumes the page’s app shell
already renders a <div id="article"></div> container to update:
navigation.addEventListener("navigate", (event) => { // Some navigations, e.g. cross-origin navigations, cannot be intercepted — // let the browser handle those normally. if (!event.canIntercept) { return; } // Don't intercept fragment navigations or downloads. if (event.hashChange || event.downloadRequest !== null) { return; }
const url = new URL(event.destination.url);
event.intercept({ async handler() { renderArticlePagePlaceholder(); const articleContent = await getArticleContent(url.pathname); renderArticlePage(articleContent); }, });});
function renderArticlePagePlaceholder() { document.querySelector("#article").innerHTML = "<p>Loading…</p>";}
async function getArticleContent(pathname) { const response = await fetch(`/api/articles${pathname}`); return response.text();}
function renderArticlePage(html) { document.querySelector("#article").innerHTML = html;}Per MDN’s navigate_event reference, the URL has already updated by the time
handler() runs, so its own example renders a placeholder immediately and swaps in
real content once it is fetched — the pattern used above.
How to detect it at runtime
Section titled “How to detect it at runtime”Feature-detect both navigation on window and NavigateEvent.prototype.intercept
before registering handlers — per the compat data above, window.navigation shipped in
Chrome 102 but intercept() did not arrive until Chrome 105, so checking window
alone lets Chrome 102–104 through and then throws a TypeError on the first
navigation. Fall back to real anchor-tag/History API navigation the browser already
performs. This example assumes markup with a <div id="article"></div> container and
in-page links (e.g. <a href="/articles/1">) already exist on the page:
function supportsNavigationApi() { return ( "navigation" in window && typeof NavigateEvent !== "undefined" && typeof NavigateEvent.prototype.intercept === "function" );}
if (supportsNavigationApi()) { navigation.addEventListener("navigate", handleAppNavigation);} else { // No usable Navigation API here — rely on regular link clicks and // History API pushState/popstate for routing instead. setupHistoryApiRouting();}
function handleAppNavigation(event) { if ( !event.canIntercept || event.hashChange || event.downloadRequest !== null || typeof event.intercept !== "function" ) { return; } const url = new URL(event.destination.url); event.intercept({ async handler() { const response = await fetch(`/api/articles${url.pathname}`); document.querySelector("#article").innerHTML = await response.text(); }, });}
function setupHistoryApiRouting() { // Intercept same-origin link clicks so they push a new History entry // instead of triggering a full page load. document.addEventListener("click", (event) => { const link = event.target.closest("a[href]"); if ( !link || link.origin !== location.origin || event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey ) { return; } event.preventDefault(); history.pushState(null, "", link.href); renderArticleFromPath(location.pathname); });
// Render the article matching the address bar on back/forward navigation. window.addEventListener("popstate", () => { renderArticleFromPath(location.pathname); });
renderArticleFromPath(location.pathname);}
async function renderArticleFromPath(pathname) { const response = await fetch(`/api/articles${pathname}`); document.querySelector("#article").innerHTML = await response.text();}Practical checklist
Section titled “Practical checklist”- Per MDN’s
navigate_eventreference, exit early (“Exit early if this navigation shouldn’t be intercepted”) for cases likeevent.hashChangeorevent.downloadRequest— not everynavigateevent should be intercepted. - Chrome’s
intercept()method replaced an earlier, removed method namedtransitionWhile— code written against Chrome 102–107 samples usingtransitionWhilewill not run on current browsers. - Per MDN’s compat data,
NavigateEvent/intercept()landed in Firefox and Safari only at version 147 and 26.2 respectively, well after Chrome — sites still need the History API fallback path for any browser below those versions. - The
navigateevent is central to all navigation types per MDN, but not all of them can be intercepted: per MDN’sNavigateEvent.canInterceptreference, cross-origin navigations are one case wherecanInterceptisfalse, so a handler must check it and let the browser handle those navigations normally instead of trying to intercept them.