Skip to content

Latest commit

 

History

History
119 lines (88 loc) · 3.3 KB

File metadata and controls

119 lines (88 loc) · 3.3 KB

next-navigation-guard

You use Next.js, and you want to show "You have unsaved changes that will be lost." dialog when user leaves page? This library is just for you!

Demo

https://layerxcom.github.io/next-navigation-guard/

How does it work?

Installation

npm install next-navigation-guard
# or
yarn install next-navigation-guard
# or
pnpm install next-navigation-guard
  • App Router: app/layout.tsx

    <html lang="en">
      <body className={`${geistSans.variable} ${geistMono.variable}`}>
        <NavigationGuardProvider>{children}</NavigationGuardProvider>
      </body>
    </html>
  • Page Router: page/_app.tsx

    export default function MyApp({ Component, pageProps }: AppProps) {
      return (
        <NavigationGuardProvider>
          <Component {...pageProps} />
        </NavigationGuardProvider>
      );
    }

Usage

  • window.confirm()

    useNavigationGuard({ enabled: form.changed, confirm: () => window.confirm("You have unsaved changes that will be lost.") })
  • Custom dialog component

    const navGuard = useNavigationGuard({ enabled: form.changed })
    
    return (
      <>
        <YourContent />
    
        <Dialog open={navGuard.active}>
          <DialogText>You have unsaved changes that will be lost.</DialogText>
    
          <DialogActions>
            <DialogButton onClick={navGuard.reject}>Cancel</DialogButton>
            <DialogButton onClick={navGuard.accept}>Discard</DialogButton>
          </DialogActions>
        </Dialog>
      </>
    )

    Note that navGuard.active is only set for client-side navigation. If the user attempts to close the tab or manually navigates to a new URL, the navigation guard will fallback to the browser's default confirmation dialog. This is an inherent limitation of modern browsers.

  • Hooking into the library's underlying state

    This is an advanced use-case if, for example, you have a special routing setup that prevents you from using the built-in NextJS router or Link component.

    import { NavigationGuardProviderContext } from 'next-navigation-guard';
    
    function SpecialLink({ href }: { href: string }) {
      const guardMapRef = useContext(NavigationGuardProivderContext);
      let guardNavigation: React.MouseEventHandler<HTMLAnchorElement> | undefined = undefined;
    
      for (const guard of guardMapRef.current.values()) {
        const { enabled, callback } = guard;
        if (!enabled({ to: href, type: 'push' })) continue;
    
        guardNavigation = (e: React.MouseEvent<HTMLAnchorElement>) => {
          e.preventDefault();
          e.stopPropagation();
    
          let confirmed = callback({ to: href, type: 'push' });
          if (typeof confirmed === 'boolean') {
            confirmed = Promise.resolve(confirmed);
          }
    
          void confirmed.then((confirmed) => {
            if (!confirmed) return;
    
            guard.enabled = () => false;
            window.location.href = href;
          });
        };
        break;
      }
    
      return <a href="/" onClick={guardNavigation}>Click Here</a>;
    }

See working example in example/ directory and its NavigationGuardToggle component.