Blank page after router.push() in Next.js?
By Flavio Copes
How to fix the blank page you get after calling router.push() in Next.js: stop returning it from render, call it in useEffect or use redirect() instead.
When working in Next.js, a blank page after you programmatically call router.push() usually means you used it as a return value. The fix is to call router.push() on its own line, and nothing else.
I had this problem too, and here’s how I solved it.
Why does this happen?
router.push() does not return JSX. In the App Router (useRouter from next/navigation) it returns nothing at all, so you get undefined. In the Pages Router (next/router) it returns a promise.
If your component returns either of those, React has nothing to render. You get a blank page.
So don’t do this:
router.push('/')
return
And don’t do this:
return router.push('/')
Do this:
router.push('/')
Where the problem usually hides
The typical case is a redirect inside the component body. You check some condition, and if it fails, you send the user somewhere else:
'use client'
import { useRouter } from 'next/navigation'
export default function Dashboard({ user }) {
const router = useRouter()
if (!user) {
return router.push('/login')
}
return <p>Welcome back</p>
}
This looks reasonable, but the component now returns undefined (or a promise, on the Pages Router) instead of JSX. Blank page.
Also, changing the route is a side effect. Side effects don’t belong in the render phase.
How to fix it (App Router)
In a Client Component, move the redirect into a useEffect() hook, and return null while the navigation happens:
'use client'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
export default function Dashboard({ user }) {
const router = useRouter()
useEffect(() => {
if (!user) {
router.push('/login')
}
}, [user, router])
if (!user) return null
return <p>Welcome back</p>
}
The effect runs after the render, calls router.push() on its own line, and React renders null in the meantime.
On the server, prefer redirect() from next/navigation inside a Server Component or Server Action instead of useRouter. That matches the pattern in how to programmatically change a route in Next.js.
Inside event handlers you don’t need any of this. A click handler is not a render, so calling it there is fine:
const handleLogout = () => {
router.push('/login')
}
Notice there’s still no return in front of it.
Pages Router (older apps)
If your app still uses pages/, the same bug and fix apply with useRouter from next/router:
import { useRouter } from 'next/router'
import { useEffect } from 'react'
export default function Dashboard({ user }) {
const router = useRouter()
useEffect(() => {
if (!user) {
router.push('/login')
}
}, [user, router])
if (!user) return null
return <p>Welcome back</p>
}
One more thing to watch for
If you skip the if (!user) return null line, the component renders its content once before the redirect completes.
The user sees a flash of the protected page, then the login page. Returning null (or a loading message) while redirecting avoids that.
Want me to talk about your product? You can sponsor this site.
Related posts about next: