v146 · Web APIs · Precommit vs Post-Commit
Precommit vs Post-Commit
The navigate event fires before the URL commits. The handler function inside event.intercept() runs after it commits. These two phases have different URL state and different APIs available — choose the right phase for each task.
Checking Navigation API support…
phase comparison
precommit — navigate event body
- ✗
location.hrefstill shows the old URL - ✗
navigation.currentEntrystill the old entry - ✓
event.destination.urlis the new URL - ✓ can call
event.preventDefault()to cancel - ✓ can call
event.intercept()to register handler - ✓ synchronous — runs before any commit
Good for:
- start loading spinner
- abort in-flight fetch requests
- cancel the navigation (preventDefault)
- set up context shared with handler
post-commit — handler() body
- ✓
location.hrefshows the new URL - ✓
navigation.currentEntryis the new entry - ✓
event.destination.urlstill available (closure) - ✗ cannot cancel — URL already committed
- ✓ can be async — awaited before navigatesuccess
- ✓ address bar already shows new URL to user
Good for:
- fetch new page content
- update app state / store
- render new route component
- stop loading spinner
live comparison
Navigate and watch URL state in each phase
Precommit snapshot
location.href: —
navigation.currentEntry: —
event.destination: —
Post-commit snapshot
location.href: —
navigation.currentEntry: —
event.destination (closure): —
Navigate between pages to see the comparison…
code
navigation.addEventListener('navigate', event => {
if (!event.canIntercept) return;
// === PRECOMMIT (navigate event body) ===
// URL has NOT changed yet
console.log('pre: location.href =', location.href); // old URL
console.log('pre: currentEntry.url =', navigation.currentEntry.url); // old URL
console.log('pre: event.destination.url =', event.destination.url); // new URL ✓
// Capture what we need for the handler (closure)
const destUrl = event.destination.url;
event.intercept({
async handler() {
// === POST-COMMIT (handler body) ===
// URL HAS changed
console.log('post: location.href =', location.href); // new URL ✓
console.log('post: currentEntry.url =', navigation.currentEntry.url); // new URL ✓
console.log('post: destUrl (closure) =', destUrl); // still works
// Now safe to load content for the new URL
const html = await fetch(destUrl).then(r => r.text());
renderContent(html);
}
});
// After event.intercept(), you're still precommit here
showLoadingSpinner(); // correct phase for this task
});