Lesson 33 of 81
next.config.ts has dozens of options and you'll touch maybe six of them — here's what those six actually control, and which two are landmines dressed up as convenience flags.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
next.config.ts (or .js) at the project root is where you override Next.js's build-time and server-startup behavior. It's plain, synchronously-evaluated configuration — no request object, no per-user logic, nothing dynamic. Anything that needs to vary by incoming request belongs in proxy.ts or a Route Handler, not here. This lesson isn't an exhaustive options reference (the framework's own docs are that); it's a tour of the options that show up in real projects often enough to be worth knowing cold.
The options fall into a few natural groups: routing shortcuts (redirects, rewrites, headers) for rules you can state declaratively without reading cookies or session state, build output shaping (output, images), and escape hatches (typescript.ignoreBuildErrors, eslint.ignoreDuringBuilds) that trade safety for an unblocked deploy.
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{ protocol: "https", hostname: "cdn.example.com", pathname: "/assets/**" },
],
},
async redirects() {
return [
{ source: "/blog/:slug", destination: "/articles/:slug", permanent: true },
];
},
async headers() {
return [
{
source: "/:path*",
headers: [{ key: "X-Frame-Options", value: "DENY" }],
},
];
},
output: "standalone",
};
export default nextConfig;
images.remotePatterns is the allowlist next/image checks before it'll optimize an external image — without a matching pattern, an <Image src="https://cdn.example.com/..." /> throws at request time rather than silently serving the raw file. redirects() and headers() are async functions returning arrays of rules Next.js bakes into its routing manifest at build time; they run for every matching request without you writing any per-request code, which is exactly why they're the right tool for a permanent URL rename but the wrong tool for "redirect only if this cookie is missing" — that conditional logic needs a real request object, which only proxy.ts has. output: "standalone" tells next build to produce a minimal, self-contained server bundle (just the files a Docker image actually needs to run node server.js), instead of assuming the deploy target has a full node_modules install available.
redirects()/rewrites()/headers() are evaluated once, at build time — they can't read the incoming request. Every rule you return has to be knowable in advance from the URL pattern alone. The moment a rule needs "is this user logged in" or "what does this cookie say," you've outgrown config and need proxy.ts.images.domains still works but is the deprecated predecessor to images.remotePatterns. domains matches by hostname only; remotePatterns lets you also constrain protocol and path, which is the safer default for anything accepting external image URLs.rewrites() is invisible to the browser; redirects() isn't. A rewrite serves different content at the same URL the user sees — useful for proxying an old path to new internals without breaking bookmarks. A redirect actually changes the URL bar and, with permanent: true, tells browsers and crawlers to update their own records (308 status) rather than just following along each time (307).ignoreBuildErrors and ignoreDuringBuilds don't fix anything — they hide the build's opinion about your code. The errors and lint violations still exist; only the build's willingness to stop you from shipping them changes.next.config.ts was an empty object for most of the build — Next.js is zero-config by default, and reaching for one of these options should be a response to a concrete need, not a starting template you fill in preemptively."You need to permanently move /blog/:slug to /articles/:slug, and separately, a marketing page needs to always redirect logged-out users elsewhere — how do you implement each?" tests whether a candidate can tell apart a build-time-knowable routing rule (config redirects()) from a request-time decision (proxy.ts) — reaching for the wrong tool for either one is a real, common design mistake, not just a style preference.