Search code examples
reactjsnext.jsnext.js13nextjs-dynamic-routing

Conditional back button in nextjs


I have an page '/message/:id' with a back button on top left which routes back using router.back() function, Which works perfectly fine when i get routed to that page from any other screen. But now i'm also sending notification which on click routes to '/message/:id' and since the page it fresh page and has no history to go back the back button doesn't work.

Is it possible to conditionally route back i.e. if there is any history then use router.back() or it should redirect me to home page ('/') Something like this

if(has page history) {
    router.back();
} else {
    router.redirect('/');
}

Solution

  • As far as I know there is no way to check previous entries in the history using the window object due to privacy concerns. A possible solution is to create a client component that will modify a context' store value upon client side navigation.


    Client Component

    This component creates a context that will be set to true when the user uses client side navigation. Therefore, if the context is truthy, the user accesses the next page from within your page. I have found a very good solution to track client side navigation changes with Next.js 13.

    "use client";
    
    import { useEffect, useState, createContext } from "react";
    
    export const OriginContext = createContext<boolean>(false);
    
    export default function OriginTracker({ children }: React.PropsWithChildren) {
      const [isWithinPage, setIsWithinPage] = useState(false);
    
      // track if the url has changed here (check link)
    
      return (
        <OriginContext.Provider value={isWithinPage}>
          {children}
        </OriginContext.Provider>
      );
    }
    

    In your root layout file

    Wrap your layout content inside the OriginTracker component. Don't worry this does not mean that everything is going to be rendered client side, since client components can receive pre-rendered server components as children.

    import OriginTracker from "components/OriginTracker";
    
    export default function RootLayout({ children }: React.PropsWithChildren) {
      return <OriginTracker>{children}</OriginTracker>;
    }
    

    Inside some other component

    Inside your other client components you can use the useContext hook to access the global state to determine where to navigate the user.

    import { useCallback } from "react";
    import { useRouter } from "next/navigation";
    
    import { OriginContext } from "components/OriginTracker";
    
    export default OtherComponent() {
      const router = useRouter();
      const isWithinPage = useContext(OriginContext);
    
      const onClick = useCallback(() => {
        if (isWithinPage) router.back();
        else router.replace('/');
      }, [isWithinPage, router]);
    
      return <button onClick={onClick}>Take me back</button>;
    }