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.
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()innext.config.jsfor static path rules
If / should always redirect, use next.config.js#
This is the cleanest answer when the rule is fixed.
// 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:
// 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.
'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:
// 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:
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()innext.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:
- Next.js App Router Guide: From Basics to Advanced Patterns
- Next.js 15 Middleware: Complete Guide to Auth, Rate Limiting, A/B Testing, and Edge Logic
- Next.js Server Actions vs API Routes: When to Use Each
- Fix Next.js revalidatePath Not Working in Server Actions
References#
Frequently Asked Questions
One email a month — no fluff
RLS gotchas, Next.js cache debugging, and the one Supabase setting that bit me last month.
Continue Reading
How to get query string parameters in Next.js
If `router.query` examples keep breaking on you, the problem is usually that you're mixing App Router and Pages Router APIs. Here is the exact fix for each case.
Why useEffect runs twice in Next.js dev
If your logs, API calls, or subscriptions fire twice in local dev, you are probably seeing React's development-only Strict Mode check. Here is what to fix and what not to panic about.
Fix "cookies() should be awaited" Error in Next.js 15
Next.js 15 broke synchronous `cookies().get()`. Every server-side call must now `await cookies()` first. Here's the precise migration — App Router pages, route handlers, Server Actions, and Supabase SSR — plus the codemod that fixes 90% of call sites automatically.
Browse by Topic
Find stories that matter to you.
