The main configuration file for a Next.js project is next.config.js, also available as .mjs or .ts. It centralizes many important settings, including allowed image domains, declarative redirects and rewrites, environment variable exposure, webpack customization, and experimental features. SEO is handled through metadata APIs: exporting a metadata object from layout.tsx or page.tsx sets static metadata such as the page title and description, while exporting an async generateMetadata function lets you fetch data dynamically and return metadata based on params or other inputs. Next.js automatically merges metadata from parent layouts with metadata defined deeper in the tree. For dynamic routes that should be statically generated at build time, the generateStaticParams function returns the list of parameter combinations to pre-render.
Images are optimized through the built-in <Image> component from next/image. It handles lazy loading by default, generates responsive sizes automatically, converts images to modern formats like WebP and AVIF, and reserves layout space using the width and height props, or the fill prop when dimensions are unknown, to prevent layout shift. The priority prop disables lazy loading for above-the-fold images that should load immediately.
Environment variables are managed through files like .env.local. Variables without a prefix are only available on the server, which keeps secrets safe. To expose a variable to the browser, prefix it with NEXT_PUBLIC_, after which it becomes accessible in client code as well. For runtime selection, Next.js supports both the default Node.js runtime and the Edge runtime, which is a lightweight JavaScript environment optimized for low latency at CDN edge locations. Middleware and Route Handlers can opt into the Edge runtime with export const runtime = 'edge', trading some Node.js API support for faster cold starts.
Finally, the framework offers multiple ways to redirect users. Inside Server Components and Server Actions you can call redirect('/path') or permanentRedirect('/path'). In next.config.js you can declare redirects declaratively, while Middleware is the right tool for conditional redirects based on the request. After a mutation, calling revalidatePath('/blog') purges the cached data for that route, and revalidateTag('posts') invalidates every cached fetch tagged with 'posts', ensuring the next render reflects the most recent state without waiting for the next timed revalidation.