Next.js Redirect from / to another page
Technology

Next.js Redirect from / to another page

If your redirect works only after render, flickers, or fires in the wrong place, you are probably using the wrong redirect API for the job. Here is the exact mapping.

2026-06-06
6 min read
Next.js Redirect from / to another page

Next.js Redirect from / to another page#

The symptom is usually one of these:

  • the home page renders and then jumps away
  • auth redirects flicker
  • redirect() works in one file but not another
  • a client-side button redirect gets copy-pasted into server code

The root cause is that Next.js has five redirect tools, and each one is for a different phase of the request.

The shortest correct answer is:

  • use redirect() for request-time server redirects
  • use useRouter().push() in a client event handler
  • use redirects() in next.config.js for static path rules

If / should always redirect, use next.config.js#

This is the cleanest answer when the rule is fixed.

ts
// next.config.mjs
const nextConfig = {
  async redirects() {
    return [
      {
        source: '/',
        destination: '/dashboard',
        permanent: false,
      },
    ]
  },
}

export default nextConfig

This happens before rendering, so there is no flash of the home page first.

If / redirects based on auth or data, use redirect()#

In the App Router, this is the server-side pattern:

tsx
// app/page.tsx
import { redirect } from 'next/navigation'

export default async function HomePage() {
  const isLoggedIn = false

  if (isLoggedIn) {
    redirect('/dashboard')
  }

  redirect('/login')
}

The current Next.js docs say redirect() returns a 307 by default, and returns 303 when used in a Server Action.

If the redirect happens after a button click, use useRouter()#

This is for event handlers in client components, not for server rendering.

tsx
'use client'

import { useRouter } from 'next/navigation'

export default function GoToDashboardButton() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  )
}

If you do not need imperative navigation, keep using <Link />.

The mistake that causes most bad redirects#

People often use the client pattern for a server problem:

tsx
// Avoid this for root-route redirect logic
'use client'

import { useEffect } from 'react'
import { useRouter } from 'next/navigation'

export default function Page() {
  const router = useRouter()

  useEffect(() => {
    router.push('/dashboard')
  }, [router])

  return null
}

This works, but it is the wrong tool for a route-level redirect. The user briefly loads /, then navigates away. Search crawlers and users both get a worse experience.

Another easy mistake: calling redirect() inside try#

The docs explicitly note that redirect() throws. If you wrap it inside try/catch, you can accidentally swallow the navigation.

Use this shape instead:

ts
import { redirect } from 'next/navigation'

export async function createProject() {
  let id: string

  try {
    id = 'abc123'
  } catch (error) {
    throw error
  }

  redirect(`/projects/${id}`)
}

Which redirect API should you pick?#

  • Known path rule for every request: redirects() in next.config.js
  • Request-time condition in server code: redirect()
  • Canonical URL changed permanently: permanentRedirect()
  • Click handler in client UI: useRouter().push()

That mapping is the part most articles skip, and it is why the same redirect can feel broken in three different ways.

For adjacent routing gotchas, keep these open:

References#

Frequently Asked Questions

|

Have more questions? Contact us

Written by

Mahdi Br
Mahdi Br

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote

One email a month — no fluff

RLS gotchas, Next.js cache debugging, and the one Supabase setting that bit me last month.