Skip to content

Conversation

chrisdavidmills
Copy link
Contributor

Description

Chromium 141 adds support for the precommitHandler option of the NavigateEvent.intercept() method. See https://chromestatus.com/feature/5134734612496384.

This PR adds documentation for it.

It also overhauls the intercept() method page in general, adding a bunch of details in a large "Description" section that was missing before. I think more work needs to be done here, but it is out of the scope of this PR for me to try to fix everything. For now, I've added a bit more information about how the intercept() features work so that the page is a bit more informative.

Motivation

Additional details

Related issues and pull requests

@chrisdavidmills chrisdavidmills requested a review from a team as a code owner October 14, 2025 08:48
@chrisdavidmills chrisdavidmills requested review from sideshowbarker and removed request for a team October 14, 2025 08:48
@github-actions github-actions bot added the Content:WebAPI Web API docs label Oct 14, 2025
@chrisdavidmills chrisdavidmills changed the title Add precommitHandler() and other info to Navigation API docs Technical review: Add precommitHandler() and other info to Navigation API docs Oct 14, 2025
@github-actions github-actions bot added the size/m [PR only] 51-500 LoC changed label Oct 14, 2025
Copy link
Contributor

github-actions bot commented Oct 14, 2025

Preview URLs

(comment last updated: 2025-10-14 15:40:51)

Copy link
Contributor

@noamr noamr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this, see comments

- : A callback function that defines what the navigation handling behavior should be. This generally handles resource fetching, and returns a promise.
- : A callback function that defines what the navigation handling behavior should be; it returns a promise. This function will run after the {{domxref("Navigation.navigate_event", "navigate")}} event has fired and the {{domxref("Navigation.currentEntry", "currentEntry")}} property has been updated.
- `precommitHandler` {{optional_inline}}
- : A callback function that defines any behavior that should occur just before the navigation has finished; it accepts a controller object as an argument and returns a promise. This function will run after the {{domxref("Navigation.navigate_event", "navigate")}} event has fired but before the {{domxref("Navigation.currentEntry", "currentEntry")}} property has been updated.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit strange to say "after navigate event has fired" because you can only intercept once the event is fired.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure I stole that wording from the explainer ;-)

But I see what you mean — the whole point is that it is run after the event is fired, so it isn't worth saying. I've updated this, and similar wording, to remove that bit, so for example:

This function will run before the {{domxref("Navigation.currentEntry", "currentEntry")}} property has been updated

- : Thrown if:
- The event was dispatched by a {{domxref("EventTarget.dispatchEvent", "dispatchEvent()")}} call, rather than the user agent.
- The navigation cannot be intercepted ({{domxref("NavigateEvent.canIntercept")}} is `false`).
- A `precommitHandler()` callback was called on a non-cancelable event ({{domxref("Event.cancelable")}} is `false`).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/called/provided

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated; thanks.


## Description

The `intercept()` method is used to implement custom navigation behavior when a link is clicked in an application. It does this via a couple of different callbacks.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not only links. Also form submissions and any type of programmatic navigation like calling pushState.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I've updated this bit to

The intercept() method is used to implement custom SPA navigation behavior when a navigation occurs, for example when a link is clicked, a form is submitted, or a programmatic navigation is initiated (using {{domxref("History.pushState()")}}, {{domxref("Window.location")}}, etc.).

It does this via a couple of different callbacks, handler() and precommitHandler().


### Handling immediate navigations with `handler()`

The `handler()` callback is generally used to handle resource fetching and updating the UI to show the content for the new page, which will typically look like this, at a basic level:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't know about "at a basic level"...

Technically, a way to describe this is that handler occurs as a response to a committed navigation, and precommitHandler occurs as a way to modify or cancel an in-flight navigation, or to perform operations while the navigation is ongoing and before it is committed.

This also works with the browser UI - e.g. the precommit handlers work before the current URL is updated in the URL bar / omnibox.

I think this distinction was a bit missing for me throughout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense, yeah. I've attempted to update the descriptive text around the two handlers to follow this pattern a bit more. Let me know what you think.


### Handling precommit actions with `precommitHandler()`

However, depending on the URL, you might not always want to commit the navigation immediately. For example, if the user navigates to a restricted page and the user is not signed in, you may want to redirect the browser to a sign-in page. This kind of scenario can be dealt with using the `precommitHandler()` callback, which will run after the {{domxref("Navigation.navigate_event", "navigate")}} event has fired but before the {{domxref("Navigation.currentEntry", "currentEntry")}} property has been updated.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not only depending on the URL. It can depend on all kinds of conditions, e.g. the source element.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this bit anyway, as per the previous comment.

The `precommitHandler()` callback takes a `controller` object as an argument, which contains a `redirect()` method. The `redirect()` method takes two parameters — a string representing the URL to redirect to, and an options object containing two parameters:

- `state` {{optional_inline}}
- : Contains any state information you want to pass along with the navigation, for example for logging or tracking purposes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point; I've added a mention and a link to the description:

Contains any state information you want to pass along with the navigation, for example, for logging or tracking purposes. The state for the navigation can subsequently be retrieved via {{domxref("NavigationHistoryEntry.getState()")}}.


Both `precommitHandler()` and `handler()` callbacks can be included inside the same `intercept()` call.

- When the `precommitHandler()` promise fulfills, the browser moves on to run any `handler()` callback present. If `precommitHandler()` rejects, `navigateerror` fires, the `committed` and `finished` promises reject, and the navigation is cancelled.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that multiple intercept calls are possible, which means something like "all precommit handlers are called first, and when all of them resolved, the navigation commits and the handler callbacks are called".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, this is good to know. I've rewritten the section like so:


Both precommitHandler() and handler() callbacks can be included inside the same intercept() call.

  1. First, the precommitHandler() handler runs.

    • When the precommitHandler() promise fulfills, the navigation commits.
    • If the precommitHandler() rejects, navigateerror fires, the committed and finished promises reject, and the navigation is cancelled.
  2. When the navigation commits, a new {{domxref("NavigationHistoryEntry")}} is created for the navigation, and its committed promise fulfills.

  3. Next, the handler() promise runs.

    • When the handler() promise fulfills and the navigatesuccess event fires, the navigation finished promise fulfills as well, to indicate the navigation is finished.
    • If handler() rejects, navigateerror fires, the finished promise rejects, and the navigation is canceled.

Note that the above process is upheld even across multiple intercept() calls on the same NavigateEvent. All precommitHandler() callbacks are called first, and when all of them resolve, the navigation commits, and all the handler() callbacks are called.


- {{domxref("NavigateEvent.intercept", "intercept()")}} takes as an argument a callback handler function returning a promise. It allows you to control what happens when the navigation is initiated. For example, in the case of an SPA, it can be used to load relevant new content into the UI based on the path of the URL navigated to.
- {{domxref("NavigateEvent.intercept", "intercept()")}} allows you to specify custom behavior for navigations, and can take the following as arguments:
- Callback handler functions allowing you to specify what happens both _when_ the navigation is initiated and _just before_ the navigation is initiated. For example, it could be used to load relevant new content into the UI based on the path of the URL navigated to, or redirect the browser to a sign-in page if the URL points to a restricted page and the user is not signed in.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The word "initiated" a bit inaccurate. It's before/when the navigation is committed.
The navigate event itself correspond to when the navigation was "initiated".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I've changed these instances to "committed".

@chrisdavidmills chrisdavidmills requested a review from noamr October 14, 2025 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Content:WebAPI Web API docs size/m [PR only] 51-500 LoC changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants