How to Fix: Error: NEXT_REDIRECT error when redirecting on server after navigating via .
The NEXT_REDIRECT error after clicking a <Link> is not a random App Router bug. It happens because a server-side redirect is being triggered during a client-side navigation path where Next.js expects a different rendering lifecycle than a full document request.
Understanding the Root Cause
In the Next.js App Router, redirect() works by throwing a special internal redirect signal. During a normal server render, Next.js catches that signal and converts it into a proper navigation response. However, when the user arrives through a <Link>, the navigation is often handled as a client transition with streamed React Server Component payloads instead of a full page load.
If the target page performs a redirect in a place where Next.js cannot safely resolve that redirect for the current transition, the framework may surface the internal NEXT_REDIRECT error instead of completing navigation. In practice, this usually appears when redirect logic is placed directly in a server-rendered page that is reached via client navigation.
The key idea is this: redirect() is not a traditional return value. It throws a framework-controlled exception. If that exception is emitted during a navigation mode or rendering phase that is not expected, you see the error.
This is why the issue often reproduces in this pattern:
- A user clicks a <Link> from one route to another.
- The destination route runs server logic and immediately calls redirect().
- The App Router receives a redirect signal during a client transition.
- The redirect is not handled the same way as a direct request, and NEXT_REDIRECT appears.
Step-by-Step Solution
The most reliable fix is to move the redirect into a route segment that Next.js can resolve consistently, or avoid using a page-level redirect as the first render target of a <Link> transition.
Option 1: Redirect before rendering the page by using middleware
If the redirect rule is deterministic, perform it in middleware so the browser never loads the intermediate page.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
if (request.nextUrl.pathname === '/redirect-page') {
return NextResponse.redirect(new URL('/dashboard', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/redirect-page']
}
Why this works:
- The redirect happens before the App Router tries to render the page.
- Both direct requests and <Link> navigations follow the same redirect path.
Option 2: Link directly to the final destination
If the intermediate page exists only to redirect, remove that hop entirely.
import Link from 'next/link'
export default function HomePage() {
return (
<Link href="/dashboard">
Go to dashboard
</Link>
)
}
This is the cleanest fix when the redirect target is known ahead of time.
Option 3: Use a client-side navigation effect when the redirect depends on client state
If the redirect depends on browser-only information, do not force it through a server redirect.
'use client'
import { useEffect } from 'react'
import { useRouter } from 'next/navigation'
export default function RedirectPage() {
const router = useRouter()
useEffect(() => {
router.replace('/dashboard')
}, [router])
return <p>Redirecting...</p>
}
Why this works:
- router.replace() is designed for client transitions.
- It avoids throwing the special server-side redirect() signal.
Option 4: If authentication is involved, redirect in the server boundary designed for auth checks
For protected routes, perform the check in middleware, a layout, or another stable server boundary instead of using a temporary page reached by <Link>.
import { redirect } from 'next/navigation'
export default async function ProtectedLayout({
children,
}: {
children: React.ReactNode
}) {
const user = await getCurrentUser()
if (!user) {
redirect('/login')
}
return <>{children}</>
}
async function getCurrentUser() {
return null
}
This pattern is usually more stable because the redirect is tied to route protection, not to an intermediate destination page.
Recommended fix for this issue pattern
If your reproduction is based on clicking a <Link> to a page whose only job is to run redirect(), the best fix is usually one of these:
- Link to the final route directly
- Move the redirect into middleware
- Use client navigation with router.replace() if the logic is client-specific
Common Edge Cases
1. Redirect inside a try/catch block
The redirect() function throws internally. If you wrap it in a broad try/catch, you may accidentally catch the redirect signal and convert it into an application error.
import { redirect } from 'next/navigation'
export default async function Page() {
try {
redirect('/dashboard')
} catch (error) {
console.error(error)
}
return <p>This should never render</p>
}
Avoid catching redirects unless you explicitly rethrow them.
2. Mixing Pages Router and App Router patterns
If part of your app still uses older routing APIs and another part uses next/navigation, redirect behavior can become confusing. Make sure your redirect logic matches the router system for that route.
3. Redirect loops
If middleware, layouts, and pages all contain navigation rules, it is easy to create a loop such as /a -> /b -> /a. Test with both direct refreshes and in-app <Link> navigation.
4. Cached or prefetched route state
<Link> can prefetch routes. If your redirect depends on rapidly changing auth or session state, the prefetched result may not match the current state at click time. In those cases, middleware or a fresh server check is safer.
5. Server Actions and route handlers
redirect() is valid in several Next.js server contexts, but not every context behaves the same. A pattern that works in a Server Action may not behave identically when used as the first render of a page reached through a <Link>.
FAQ
Why does the redirect work on direct refresh but fail after clicking a Link?
A direct refresh is a full request, so Next.js can handle the server-thrown redirect() during normal request processing. A <Link> often triggers a client transition with streamed server payloads, which follows a different execution path.
Should I stop using redirect() in the App Router?
No. redirect() is the correct API for many server-side cases. The problem is usually where it is called. Use it in stable server boundaries such as protected layouts, route handlers, or places where Next.js fully controls the request lifecycle.
What is the safest fix if the page only exists to redirect?
Do not navigate to that page first. Either link directly to the final destination or move the redirect to middleware. That removes the fragile intermediate server redirect during a client transition.
In short, the fix is not about suppressing NEXT_REDIRECT. It is about restructuring navigation so the redirect occurs in the right layer: before rendering, at a stable server boundary, or fully on the client depending on the use case.