You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Aug 21, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Aug 21, 2026. It is now read-only.
The scenario shown in #121 uses navigation.navigate(newURL, { replace: true }) in the middle of a navigate handler, to perform a "client-side redirect".
However, this is not optimal, as doing so makes the original navigation count as interrupted, and thus a "failure", with an "AbortError"DOMException as the failure reason. Notably it fires navigateerror on navigation, rejects navigation.transition.finished, and rejects the promise returned by navigation.navigate() if the navigation started that way.
There may be other downsides, e.g. the very-related discussion in #5 talks about the problems with losing same-siteness (for Sec-Same-Site) and form data. However, for purely client-side redirects I don't think those particular concerns apply. I guess maybe losing the userInitiated bit is in this vein, though.
We may want to contemplate a first-class redirect API, perhaps on the NavigateEvent object. The semantics would be something like a replace, but with the additional pieces:
navigation.transition.finished delays settling until the new navigation settles
navigation.navigate()'s return value delays settling until the new navigation settles
navigation.transition.rollback() during the new navigation rolls back both navigations.
event.signal for the original event does not fire abort.
Maybe we carry over event.userInitiated
Maybe we carry over event.info and event.formData unless told not to?
There is still a period where navigation.curentEntry is the intermediate page (e.g. if you're navigating to /b and want to redirect to /c, navigation.currentEntry will briefly be on /b before eventually replacing with /c). There's no way of getting around that.
then you should not use { replace: true }, since at the time the (*) line is running, the navigation hasn't happened yet: we're still in the synchronous phase of event dispatch (despite being inside an async function).
Whereas if you're asynchronously trying to do a redirect, as in this example
e.transitionWhile((async()=>{if(awaitshouldRedirect()){// Note the awaitawaitnavigation.navigate("/redirected");// (*)}else{// ...}})());
then you do need to use { replace: true }, because by that point the navigation has gone through and you're on the page that needs to get replaced.
This seems error-prone and fragile, so adding a first-class helper to smooth this over would be a good idea.
If you're synchronously trying to do the redirect, as in this example:
This example also causes annoying uncaught exceptions, since the first navigation being canceled causes transitionWhile() to throw an exception (since it's called on a canceled event). See https://crbug.com/1224413.
A concrete use-case for this might be for SPA app initial navigation. When going directly to a guarded URL, that navigation might be rejected. If the application developer wants to deny the the navigation but doesn't provide a redirect, how should the framework's router handle this? Right now, this can result in "blank" screens on initial page load.
Let's say the framework's router allows the application developer to specify a fallback to redirect to if the initial navigation fails, for example the home page. It would also be interested in tracking any redirects associated with that initial navigation so it doesn't eagerly navigate to the home page fallback when it should instead allow any associated redirects to complete.
Edit: would the first class redirect support multiple redirects? i.e., navigate to a-> redirects to b -> redirects to c
It would also be interested in tracking any redirects associated with that initial navigation so it doesn't eagerly navigate to the home page fallback when it should instead allow any associated redirects to complete.
I don't quite understand that part; which redirects exist in this setup, besides the one from a guarded URL to the fallback homepage URL?
Edit: would the first class redirect support multiple redirects? i.e., navigate to a-> redirects to b -> redirects to c
Yes, I think that's the ideal.
To summarize my current thinking of what we could do for redirects: navigation.transition.redirect(url, { info }) would have the following impacts:
If the navigate event has not finished firing:
Cancel the navigate event (like preventDefault()), but without firing navigateerror or rejecting any promises or firing abort on the AbortSignal.
Start a new navigation, similar to navigation.navigate(url, { info }).
Update navigation.transition in place to track that navigation, instead of replacing the object like navigation.navigate() normally would. (This ensures anyone watching navigation.transition.finished gets notified at the right time.)
Make any { committed, finished } promises returned from any original navigation tie themselves to the new navigation.
Otherwise, if the navigate event has finished firing but we're still in the transition phase:
Start a new navigation, similar to navigation.navigate(url, { info, history: "replace" }). (Note the replace!!) Similarly don't fire navigateerror or reject any promises or fire abort, even though we are interrupting a transitioning navigation.
Update navigation.transition in place to track that navigation. I think this includes updating navigation.transition.navigationType (to replace) and navigation.transition.from (to the being-replaced intermediate entry).
Update any { finished } promise returned from the original navigation, similar to the above. (But we cannot update committed since it already settled.)
Otherwise, immediately reject. (This can only happen if you stored navigation.transition somewhere and called storedTransition.redirect(...) later; the transition property will get nulled out after the transition phase.)
All of these steps should work if done more than once.
It would also be interested in tracking any redirects associated with that initial navigation so it doesn't eagerly navigate to the home page fallback when it should instead allow any associated redirects to complete.
I don't quite understand that part; which redirects exist in this setup, besides the one from a guarded URL to the fallback homepage URL?
@domenic The fallback URL would only want to be applied if the initial navigation totally failed. There might be user-defined redirects associated with that navigation and wouldn't want to perform the fallback navigation if a different redirect succeeded. I believe the properties of the navigation you just outlined would make this task easy.
I was playing around with the API and I noticed that event.respondWith() and event.redirect() do not exist yet. I believe that allowing to redirect a navigation is crucial for SPAs because it's a mechanism that already exists in navigation guards and because it would allow for SPA routers to listen to navigatesuccess and navigateerror events to determine when a navigation is done.
Having a native way to redirect within a navigate event is the last missing piece for users to freely use the native browser API instead of having to use the router's API. I think this would be a huge win for this browser API. Nowadays, routers enforce users to go through routers' methods (or components like <RouterLink />) to ensure a consistent state (there is no way to consistently detect a history.pushState()) and since they control when history.pushState() is called, they allow redirecting within a navigation and it still counts as one single call to router.push() (or router.navigate()).
By not being able to redirect within a navigation, we break this flow and one navigation can trigger multiple ones. Of course, this could also be the new expected behavior and routers could go this path and release with a breaking change.
Related to #5, To me, rewriting and redirected are the same in the context of an SPA router, sometimes you change the URL, sometimes you add stuff to state or info
@posva for sure, I ran into the same thing prototyping things a couple weeks ago. We were able to agree on the simplest case for redirect with uncommitted deferred navigations. I hadn’t considered redirecting with state or info though… I could see how that might be useful when all baseline browsers have the navigation api implemented and navigations no longer have to be initiated through the router APIs.
It's worth noting that I'm only considering uncommitted deferred navigations because it seems like everybody agreed that was the most common use case for SPA routers in #66 and also I thought it would be a requirement for redirects, otherwise the URL would change multiple times.
The way things are outlined at #124 (comment) should work out well for the needs of Vue Router
#279 is our current plan for redirects, already implemented in Chromium behind a flag. I kept this open to track potential work on "rollbacks" (separate from redirects), but maybe that is confusing.
Let me close this, and perhaps if you think there is something in the current design in the explainer that is not satisfactory, then you could open a specific issue with a suggestion? For example, it sounds like maybe we want to allow redirects to not only modify the URL, but also the info, and maybe the state? Writing that up in a clear issue with some sample use cases would be very helpful.
Thanks, I will open an issue regarding the info and state in a redirect.
Regarding event.redirect() itself(), I tried it on the Dev version and it doesn't follow the: it doesn't trigger a new navigation like navigation.navigate() as in navigate is not triggered and cannot be intercepted again. It only allows to change the final URL that is committed and just once. In SPAs routers, when a navigation guard redirect, that new redirection goes through navigation guards again and can redirect as many times as needed. As mentioned in #124 (comment), I think not being able to handle redirect this way would mean discouraging the use of event.redirect() in intercepted navigation when they use an SPA router and limiting the extensibility of the Navigation API.
Is this meant to be like this or are they plans to change it?
Activity
domenic commentedon Jun 28, 2021
I just realized something that ups the priority here.
If you're synchronously trying to do the redirect, as in this example:
then you should not use
{ replace: true }, since at the time the (*) line is running, the navigation hasn't happened yet: we're still in the synchronous phase of event dispatch (despite being inside an async function).Whereas if you're asynchronously trying to do a redirect, as in this example
then you do need to use
{ replace: true }, because by that point the navigation has gone through and you're on the page that needs to get replaced.This seems error-prone and fragile, so adding a first-class helper to smooth this over would be a good idea.
domenic commentedon Jan 12, 2022
This example also causes annoying uncaught exceptions, since the first navigation being canceled causes
transitionWhile()to throw an exception (since it's called on a canceled event). See https://crbug.com/1224413.Move post-v1 stuff out of the spec/main proposal for now
Move post-v1 stuff out of the spec/main proposal for now
atscott commentedon Apr 23, 2022
A concrete use-case for this might be for SPA app initial navigation. When going directly to a guarded URL, that navigation might be rejected. If the application developer wants to deny the the navigation but doesn't provide a redirect, how should the framework's router handle this? Right now, this can result in "blank" screens on initial page load.
Let's say the framework's router allows the application developer to specify a fallback to redirect to if the initial navigation fails, for example the home page. It would also be interested in tracking any redirects associated with that initial navigation so it doesn't eagerly navigate to the home page fallback when it should instead allow any associated redirects to complete.
Edit: would the first class redirect support multiple redirects? i.e., navigate to a-> redirects to b -> redirects to c
domenic commentedon Apr 25, 2022
I don't quite understand that part; which redirects exist in this setup, besides the one from a guarded URL to the fallback homepage URL?
Yes, I think that's the ideal.
To summarize my current thinking of what we could do for redirects:
navigation.transition.redirect(url, { info })would have the following impacts:navigateevent has not finished firing:preventDefault()), but without firingnavigateerroror rejecting any promises or firingaborton theAbortSignal.navigation.navigate(url, { info }).navigation.transitionin place to track that navigation, instead of replacing the object likenavigation.navigate()normally would. (This ensures anyone watchingnavigation.transition.finishedgets notified at the right time.){ committed, finished }promises returned from any original navigation tie themselves to the new navigation.navigateevent has finished firing but we're still in the transition phase:navigation.navigate(url, { info, history: "replace" }). (Note the replace!!) Similarly don't firenavigateerroror reject any promises or fireabort, even though we are interrupting a transitioning navigation.navigation.transitionin place to track that navigation. I think this includes updatingnavigation.transition.navigationType(to replace) andnavigation.transition.from(to the being-replaced intermediate entry).{ finished }promise returned from the original navigation, similar to the above. (But we cannot updatecommittedsince it already settled.)navigation.transitionsomewhere and calledstoredTransition.redirect(...)later; thetransitionproperty will get nulled out after the transition phase.)All of these steps should work if done more than once.
atscott commentedon Apr 25, 2022
@domenic The fallback URL would only want to be applied if the initial navigation totally failed. There might be user-defined redirects associated with that navigation and wouldn't want to perform the fallback navigation if a different redirect succeeded. I believe the properties of the navigation you just outlined would make this task easy.
Add an explainer for navigateEvent.redirect()
Add an explainer for navigateEvent.redirect()
posva commentedon Feb 26, 2025
I was playing around with the API and I noticed that
event.respondWith()andevent.redirect()do not exist yet. I believe that allowing to redirect a navigation is crucial for SPAs because it's a mechanism that already exists in navigation guards and because it would allow for SPA routers to listen tonavigatesuccessandnavigateerrorevents to determine when a navigation is done.Having a native way to redirect within a
navigateevent is the last missing piece for users to freely use the native browser API instead of having to use the router's API. I think this would be a huge win for this browser API. Nowadays, routers enforce users to go through routers' methods (or components like<RouterLink />) to ensure a consistent state (there is no way to consistently detect ahistory.pushState()) and since they control whenhistory.pushState()is called, they allow redirecting within a navigation and it still counts as one single call torouter.push()(orrouter.navigate()).By not being able to redirect within a navigation, we break this flow and one navigation can trigger multiple ones. Of course, this could also be the new expected behavior and routers could go this path and release with a breaking change.
Related to #5, To me, rewriting and redirected are the same in the context of an SPA router, sometimes you change the URL, sometimes you add stuff to
stateorinfodomenic commentedon Feb 26, 2025
Did you see #279?
respondWith()was replaced several times, and its current name isintercept().posva commentedon Feb 26, 2025
I did see #279. Since it's not implemented and the issues are open I understood they are still in discussion and not sure to be added
Thanks! I wasn't able to follow all the changes 😅
atscott commentedon Feb 26, 2025
@posva for sure, I ran into the same thing prototyping things a couple weeks ago. We were able to agree on the simplest case for redirect with uncommitted deferred navigations. I hadn’t considered redirecting with
stateorinfothough… I could see how that might be useful when all baseline browsers have the navigation api implemented and navigations no longer have to be initiated through the router APIs.posva commentedon Feb 26, 2025
It's worth noting that I'm only considering uncommitted deferred navigations because it seems like everybody agreed that was the most common use case for SPA routers in #66 and also I thought it would be a requirement for redirects, otherwise the URL would change multiple times.
The way things are outlined at #124 (comment) should work out well for the needs of Vue Router
domenic commentedon Feb 27, 2025
#279 is our current plan for redirects, already implemented in Chromium behind a flag. I kept this open to track potential work on "rollbacks" (separate from redirects), but maybe that is confusing.
Let me close this, and perhaps if you think there is something in the current design in the explainer that is not satisfactory, then you could open a specific issue with a suggestion? For example, it sounds like maybe we want to allow redirects to not only modify the URL, but also the info, and maybe the state? Writing that up in a clear issue with some sample use cases would be very helpful.
posva commentedon Feb 27, 2025
Thanks, I will open an issue regarding the info and state in a redirect.
Regarding
event.redirect()itself(), I tried it on the Dev version and it doesn't follow the: it doesn't trigger a new navigation likenavigation.navigate()as innavigateis not triggered and cannot be intercepted again. It only allows to change the final URL that is committed and just once. In SPAs routers, when a navigation guard redirect, that new redirection goes through navigation guards again and can redirect as many times as needed. As mentioned in #124 (comment), I think not being able to handle redirect this way would mean discouraging the use ofevent.redirect()in intercepted navigation when they use an SPA router and limiting the extensibility of the Navigation API.Is this meant to be like this or are they plans to change it?
domenic commentedon Feb 27, 2025
Please file new issues for any such concerns!