Search code examples
javascriptreactjsnext.jsnext-router

use NextRouter outside of React component


I have a custom hook that will check whether you are logged in, and redirect you to the login page if you are not. Here is a pseudo implementation of my hook that assumes that you are not logged in:

import { useRouter } from 'next/router';

export default function useAuthentication() {

  if (!AuthenticationStore.isLoggedIn()) {
    const router = useRouter();
    router.push('/login'); 
  }
}

But when I use this hook, I get the following error:

Error: No router instance found. you should only use "next/router" inside the client side of your app. https://err.sh/vercel/next.js/no-router-instance

I checked the link in the error, but this is not really helpful because it just tells me to move the push statement to my render function.

I also tried this:

// My functional component
export default function SomeComponent() {

  const router = useRouter();
  useAuthentication(router);

  return <>...</>
}

// My custom hook
export default function useAuthentication(router) {

  if (!AuthenticationStore.isLoggedIn()) {
    router.push('/login');
  }
}

But this just results in the same error.

Is there any way to allow routing outside of React components in Next.js?


Solution

  • The error happens because router.push is getting called on the server during SSR on the page's first load. A possible workaround would be to extend your custom hook to call router.push inside a useEffect's callback, ensuring the action only happens on the client.

    import { useEffect } from 'react';
    import { useRouter } from 'next/router';
    
    export default function useAuthentication() {
        const router = useRouter();
    
        useEffect(() => {
            if (!AuthenticationStore.isLoggedIn()) {
                router.push('/login'); 
            }
        }, [router]);
    }
    

    Then use it in your component:

    import useAuthentication from '../hooks/use-authentication' // Replace with your path to the hook
    
    export default function SomeComponent() {
        useAuthentication();
    
        return <>...</>;
    }