Replies: 27 comments 33 replies
-
I'm looking for the same feature or at least an official recommendation on why we shouldn't implement this feature to show to my PO and UX 🤣 Apparently you should be able to implement this feature by listening to I also found an article of someone patching react router to implement this feature However I haven't tried any of these yet. My company has not migrated to NextJs yet other peope are asking for this feature at:
|
Beta Was this translation helpful? Give feedback.
-
@fredericrous Even then with those proposed hacks, it doesn't solve the problem for NextJs 13, where the router events have been removed entirely. |
Beta Was this translation helpful? Give feedback.
-
did you find a solution? |
Beta Was this translation helpful? Give feedback.
-
Can't you use client side component that handle Just that Component.. Return null |
Beta Was this translation helpful? Give feedback.
-
Looks like it's not yet supported on the app dir
Could there be any alternative to prevent it? None of the above are working solutions for the app dir. |
Beta Was this translation helpful? Give feedback.
-
I recently came across the same problem, and did not find an official solution for this case either 😅 In short, it creates a mutation observer to track down all While what I had done feels incredibly hacky, is definitely not well-tested and may or may not have an unpleasant smell, I think it works in my case and solves the problem for the time being for me. Would be happy if I could get some feedback on this or if this helps someone else as well :) |
Beta Was this translation helpful? Give feedback.
-
I want an official answer.. |
Beta Was this translation helpful? Give feedback.
-
Hello, I've got around to publishing my gist above as an npm package with a decent README that showcases the usage for blocking navigation! Please check out the repo here, you could give it a try while we are waiting for the official solution :) |
Beta Was this translation helpful? Give feedback.
-
I have the same problem, we are mirgrating from page router as encouraged by vercel team but we cannot finish the migration due to this lack of feature. An official answer is appreciated! |
Beta Was this translation helpful? Give feedback.
-
I don't know why there is no official answer to this as of now. This is needed for complex use-cases such as building an online exam tool where navigating away from the page must be blocked or even complex forms. |
Beta Was this translation helpful? Give feedback.
-
This is very weird to me. Why would you remove them? |
Beta Was this translation helpful? Give feedback.
-
See if this helps, https://usehooks.com/usepageleave, they say
|
Beta Was this translation helpful? Give feedback.
-
There are so many discussion threads on this issue all over this repo and yet no official answer from vercel on any of them. |
Beta Was this translation helpful? Give feedback.
-
@timneutkens @Timer can we have some feedback on this? |
Beta Was this translation helpful? Give feedback.
-
Intercept route changes at the NextJS app router modeDemo:[codeSondbox] cf6e2e9c42a4f29b1dacadffb58c9a1f_723815601830_v_1702122801840414.mp4source code: https://github.com/cgfeel/next.v2/tree/master/routing-file/src/app/leaving/proxy Use this Provider in your layout at the app root directory:https://github.com/cgfeel/next.v2/blob/master/routing-file/src/components/proxyProvider/index.tsx 'use client'
import { usePathname, useSearchParams } from "next/navigation";
import Script from "next/script";
import { FC, PropsWithChildren, createContext, useEffect, useState } from "react";
const ProxyContext = createContext<ProxyInstance>([undefined, () => {}]);
const ProxyProvider: FC<PropsWithChildren<{}>> = ({ children }) => {
const [tips, setTips] = useState<string|undefined>();
const msg = tips === undefined ? tips : (tips||'Are you sure want to leave this page?');
const pathname = usePathname();
const searchParams = useSearchParams();
const url = [pathname, searchParams].filter(i => i).join('?');
useEffect(() => {
setTips(undefined);
}, [url, setTips]);
useEffect(() => {
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
if (msg === undefined) return msg;
event.preventDefault();
event.returnValue = msg;
return msg;
};
const script = document.getElementById('proxy-script');
if (script) {
script.dataset.msg = msg||'';
script.dataset.href = location.href;
}
window.addEventListener("beforeunload", handleBeforeUnload);
return () => {
window.removeEventListener("beforeunload", handleBeforeUnload);
}
}, [msg]);
return (
<ProxyContext.Provider
value={[msg, setTips]}
>
<Script
strategy="afterInteractive"
id="proxy-script"
dangerouslySetInnerHTML={{
__html: `(() => {
const originalPushState = history.pushState.bind(history);
let currentPoint = 0;
let point = 0;
window.history.pushState = function(state, title, url) {
state.point = ++point;
currentPoint = point;
originalPushState(state, title, url);
};
const originalReplaceState = history.replaceState.bind(history);
window.history.replaceState = function(state, title, url) {
state.point = currentPoint;
originalReplaceState(state, title, url);
};
window.addEventListener('popstate', function (event) {
const { state: nextState } = event;
const isback = currentPoint > nextState.point;
currentPoint = nextState.point;
const script = document.getElementById('proxy-script');
if (!script || location.href === script.dataset.href) return;
const msg = script.dataset.msg||'';
const confirm = msg == '' ? true : window.confirm(msg);
if (!confirm) {
event.stopImmediatePropagation();
isback ? history.forward() : history.back();
}
});
})()`,
}}
></Script>
{children}
</ProxyContext.Provider>
);
};
export type ProxyInstance = [
string|undefined, (tips?: string) => void
]
export { ProxyContext };
export default ProxyProvider; |
Beta Was this translation helpful? Give feedback.
-
I created a solution for this in the end by creating an You can read how to implement the same in your app here: How-to: Handle unsaved page changes with NextJS app router |
Beta Was this translation helpful? Give feedback.
-
Hmmm... still nothing on this. This is so damn lame. It's been awhile. Is there really no update on this yet? |
Beta Was this translation helpful? Give feedback.
-
I am looking for a solution for this as well |
Beta Was this translation helpful? Give feedback.
-
I made an function useInterceptAppRouter<TMethod extends keyof AppRouterInstance>(
method: TMethod,
interceptFn: (original: AppRouterInstance[TMethod], ...args: Parameters<AppRouterInstance[TMethod]>) => void,
) {
const appRouter = use(AppRouterContext);
useEffect(() => {
if (!appRouter) throw new Error("useInterceptAppRouter must be used within an App Router context");
const originalMethod = appRouter[method];
appRouter[method] = ((...args: Parameters<AppRouterInstance[TMethod]>) => {
console.log("intercepting app router method:", method, "with args:", args);
interceptFn(originalMethod, ...args);
}) as AppRouterInstance[TMethod];
return () => {
appRouter[method] = originalMethod;
};
}, [appRouter, method, interceptFn]);
} And this is how I use it for enabling view transitions: function ViewTransitioner() {
const handleViewTransition = <TArgs extends unknown[]>(
originalMethod: (...args: TArgs) => unknown,
...args: TArgs
) => {
if (!document.startViewTransition) {
originalMethod(...args);
return;
}
document.startViewTransition(() => {
originalMethod(...args);
});
};
useInterceptAppRouter("back", handleViewTransition);
useInterceptAppRouter("forward", handleViewTransition);
useInterceptAppRouter("push", handleViewTransition);
useInterceptAppRouter("refresh", handleViewTransition);
useInterceptAppRouter("replace", handleViewTransition);
return null;
} |
Beta Was this translation helpful? Give feedback.
-
I would also hope to get an official response from the Next team on this, as there are some valid use cases where this functionality would be useful. However, this is #49532 (comment) I made on a related issue. TL;DR persists user's unfinished form state on session storage instead of preventing route transition. Better UX, smaller tech effort. |
Beta Was this translation helpful? Give feedback.
-
Is there any solution for it now? |
Beta Was this translation helpful? Give feedback.
-
how about a wrapper component for anchors and buttons which use e.g. router.push that would prevent redirect if some unwanted condition is true? |
Beta Was this translation helpful? Give feedback.
-
Any update on this issue, |
Beta Was this translation helpful? Give feedback.
-
Asking the same question (i.e. how to listen to the |
Beta Was this translation helpful? Give feedback.
-
Finally I created a library for showing page leave confirmation in Next.js . https://github.com/LayerXcom/next-navigation-guard It works with both App Router and Pages Router. How it works is described here: |
Beta Was this translation helpful? Give feedback.
-
How can it be that after more than 1.5 years there is no non-hacky solution? Come on, Vercel |
Beta Was this translation helpful? Give feedback.
-
I also implemented a monkey-patching solution. Similar to the ones above but a bit simpler.
And how to use it where you need:
In the example above, the navigation is blocked for 5s. How it works? Also, you can simply subscribe to router change events to do what you want and return nothing. For example, trigger some Google Analytic events or whatever. This approach does not require any additional usage of Context API. The hook is returned from the self-invoked function, which patches router events once only and also stores subscription callbacks. This solution also does not solve a case when you use browser back/forward buttons. I think the only way to solve this is to call https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event |
Beta Was this translation helpful? Give feedback.
-
Summary
With the removal of router events in the NextJs 13 apps, how are we supposed to block navigation based on a conditional flag?
Ideally we want a custom UI alert to appear when the user tries to navigate within the application, as well as cover the case of a browser navigation (back/forward/refresh) and have the browsers default alert prompt appear.
Both cases should either accept/decline the navigation and either preserve or update the URL based on which action was taken.
There's no mention of this anywhere and it seems like none of the "Advanced Routing Patterns" solve this.
Additional information
No response
Example
No response
Beta Was this translation helpful? Give feedback.
All reactions