3

I want to wrap my whole application so that it is not accessible unless the user is logged in. I managed to redirect the user to the login page if the user is not logged in, however, i still see a flash of the private route before the redirection occurs. How can this be avoided?

poca
  • 3,630
  • 3
  • 13
  • 19
  • possible duplicate of https://stackoverflow.com/questions/64662486/how-do-you-deal-with-public-and-private-routes-in-a-nextjs-app/64686516#64686516 – Andrew Zheng Mar 21 '21 at 01:28
  • Yes @AndrewZheng i tried that approach but for some reason, i still see a flash of the private page before it redirects – poca Mar 21 '21 at 01:55
  • @poca I always do this with swr which blocks rendering until the condition is met. I get the user authenticated with swr and check if the user exists or not then I redirect – enoch Mar 21 '21 at 03:10

1 Answers1

6

Because NextJS is server-side rendered, you either need to check the authentication with getServerSideProps or display a loading indicator on the front-end before the redirect.

Checking authentication client-side

Create a wrapper component and put it inside your _app.js file. By showing a loading component while the user is still being authenticated, you prevent the private dashboard from showing. And note: because Next.js is server-side rendered, the HTML will always show up before the JS is rehydrated. That means, first paint will always occur before the redirect starts.

import { useRouter } from 'next/router'

export const AuthCheck = (props) => {
  const router = useRouter()
  const user = useUser() // you need to implement this. In this example, undefined means things are still loading, null means user is not signed in, anything truthy means they're signed in

  if (typeof window !== 'undefined' && user === null) router.push('/sign-in')

  if(!user) return <Loading /> // a loading component that prevents the page from rendering
   
  return props.children
}

Then in your _app.js

const MyApp = ({ Component, pageProps }) => {
  return (
    <AuthCheck>
      <Component {...pageProps} />
    </AuthCheck>
  )
}

export default MyApp

Checking authentication server-side

Assuming you already have code setup to check authentication server-side, you can use this pattern. Note: you'll need to add this to every single page. getServerSideProps does not work with _app.js or _document.js

export const getServerSideProps = async () => {
  const isAuthenticated = await checkAuthentication() // you need to implement this

  if (!isAuthenticated) {
    return {
      redirect: { destination: '/sign-in', permanent: false },
    }
  }
}
Nick
  • 5,108
  • 2
  • 25
  • 58
  • Thanks Nick!, but after implementing this 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' – poca Mar 21 '21 at 03:49
  • You're right, forgot to add the check to see if the code is running in the browser. Updating the answer now. – Nick Mar 21 '21 at 03:55
  • 1
    The solution provided for checking the authentication on the client-side would always take the user to `/sign-in` even if the user tries to navigate to a page that is public (the user doesn't have to sign in). For example, if I want to navigate to `sign-up`, this condition will kick in and take me to `/sign-in`. It also blocks the 404 route recognition. Problem one can be solved by providing an array of public routes but doesn't seem like a clean solution – shet_tayyy Apr 15 '22 at 11:29