Next.js 45: Redirects & Rewrites
easy⏱ 5 mincoursenextjs
Config-time vs runtime
redirects()/rewrites() in next.config.js are evaluated once, at build/start time, against a fixed list of source → destination patterns — great for permanent URL restructuring (permanent: true → 308) or legacy path support. redirect() from next/navigation, used inside a component, runs per-request and can react to anything you can compute during render.
// next.config.js — build-time, pattern based
module.exports = {
async redirects() {
return [
{ source: '/old-pricing', destination: '/pricing', permanent: true },
{ source: '/docs/:path*', destination: '/help/:path*', permanent: false },
];
},
async rewrites() {
return [{ source: '/blog/:slug', destination: '/posts/:slug' }];
},
};
// inside a Server Component — runtime, conditional
import { redirect } from 'next/navigation';
export default async function CheckoutPage() {
const user = await getUser();
if (!user.isPremium) redirect('/pricing');
return <Checkout />;
}
Match both kinds
Click through the sample paths and confirm /old-pricing and /docs/getting-started match a config rule (showing 307/308 and the resolved destination) while /about doesn't. Then toggle isPremium and render /checkout to see the conditional redirect() fire only when it's off.
Prefer next.config.js when you can
A config-time redirect is cheaper (no component even renders) and easier to audit as a flat list. Reach for a runtime redirect() only when the destination genuinely depends on something you can't know ahead of time, like the current user's data.