Skip to content

Instantly share code, notes, and snippets.

@yuvalkarmi
Last active April 30, 2024 13:42
Show Gist options
  • Save yuvalkarmi/ab7944b2da693c71cf697db6a47e5b5d to your computer and use it in GitHub Desktop.
Save yuvalkarmi/ab7944b2da693c71cf697db6a47e5b5d to your computer and use it in GitHub Desktop.
How to block a user from navigating away in NextJS when clicking on Link component
/*
In standard web apps, when users navigate away to a different page or website or attempt to refresh a page,
we can use the window event listener `beforeunload` to help users avoid losing the work.
However, when a user clicks on a native NextJS <Link> component, the navigation is not intercepted
because behind the scenes, no page changes are actually happening.
To intercept user navigation in a NextJS app, we can subscribe to the
router routeChangeError event and throw an error.
Note: we must throw an Error string - not a `new Error()` - which is why in the code below, we have
// eslint-disable-next-line no-throw-literal
above the line throwing the error.
*/
import Router from "next/router";
import { useEffect } from 'react';
const YourComponent = () => {
// Relevent code begin
const shouldBlockNavigation = true; // set this up however you want
useEffect(() => {
const nativeBrowserHandler = (event) => {
if (shouldBlockNavigation) {
event.preventDefault();
event.returnValue = '';
}
};
const nextNavigationHandler = (url) => {
if (shouldBlockNavigation) {
if (!window.confirm('Navigate away? Changes you made may not be saved.')) {
Router.events.emit('routeChangeError')
// eslint-disable-next-line no-throw-literal
throw "Abort route change by user's confirmation."
}
}
};
window.addEventListener('beforeunload', nativeBrowserHandler);
Router.events.on("beforeHistoryChange", nextNavigationHandler);
// On component unmount, remove the event listener
return () => {
window.removeEventListener('beforeunload', nativeBrowserHandler);
Router.events.off("beforeHistoryChange", nextNavigationHandler);
};
}, [shouldBlockNavigation]);
// Relevent code end
return <div>Your Component</div>
}
@tranduybau
Copy link

@huongnguyenduc yah I know bro. I saw that from the start. But in that case, you better commented something like : Didn't work on my app using new App Router. The version is 13.4.X. Can you help me with that?. Here we don't know who you are and don't care about it. Be patient and polite. Sure, You can add my telegram and I can help you. Here you go: https://t.me/bautd

@huongnguyenduc
Copy link

@trdbau Oh sorry if I commented something badly. Thank you very much for your feedback!! I appreciate your quick responses so much ^^

@tranduybau
Copy link

tranduybau commented Jun 22, 2023

@huongnguyenduc NP. Have a good day. Tell us if you find something in the future 😄

@DiegoMcDipster
Copy link

DiegoMcDipster commented Jun 29, 2023

I'd used one of the previous (pretty messy and hacky) solutions but that suddenly stopped since upgrading to Next 13. So, I came back to the issue and found this much cleaner solution.

However, I do have an issue that I'm hoping you can help me with. In my _app.ts I am using the router events for my loading animation, like so:

useEffect(() => {
    const handleRouteChangeStart = () => {
      setIsLoading(true);
    };

    const handleRouteChangeComplete = () => {
      setIsLoading(false);
    };

    const handleRouteChangeError = () => {
      setIsLoading(false);
    };

    router.events.on("routeChangeStart", handleRouteChangeStart);
    router.events.on("routeChangeComplete", handleRouteChangeComplete);
    router.events.on("routeChangeError", handleRouteChangeError);

    return () => {
      router.events.off("routeChangeStart", handleRouteChangeStart);
      router.events.off("routeChangeComplete", handleRouteChangeComplete);
      router.events.off("routeChangeError", handleRouteChangeError);
    };
  }, [router.events]);

This seems to take precedence over your event handlers on the component. So,

  1. browser back button: does NOT display the route change warning
  2. next/router.back() does NOT display the route change warning
  3. browser refresh DOES display the warning

I'm pretty new to all this, so please let me know if my loading animation code is not a best practice. Otherwise, what could be a possible solution?

@sahmed007
Copy link

So frickin' glad I came across this. This worked for me on Next 13.2.4.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment