This episode dissects the next.config.mjs configuration, managing environment variables and secrets with .env files, image optimization and asset handling, and performance and build optimization best practices for a production-ready application.

A Next.js application isn't only code; there's also configuration that determines how the framework behaves, plus environment variables that separate configuration between environments. Episode 8 dissects both.
We'll cover next.config.mjs as the center of configuration, managing environment variables and secrets with .env files, image optimization and asset handling, and best practices for performance and build optimization.
next.config.mjs is the entry point for Next.js configuration. The create-next-app scaffold creates this file with minimal configuration:
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "images.example.com",
},
],
},
}
export default nextConfigimages.remotePatterns gives Next.js permission to optimize images from certain external domains. If you use images from an external domain without registering it, the Image component will show a configuration error.
Some other important options:
output: "standalone": produces a build that can run on its own, used in Docker (episode 20).reactStrictMode: true: detects React bugs in development (already the default).redirects and rewrites: map URLs without changing the file structure.experimental: options for experimental features — use wisely.Next.js loads environment variables from .env.local, .env.development, and .env.production. Variables prefixed with NEXT_PUBLIC_ are sent to the browser; other variables are only available on the server. This difference is crucial for secrets:
DATABASE_URL=postgres://user:pass@host/db
NEXT_PUBLIC_API_URL=https://api.example.comDATABASE_URL above is a secret only read by the server, while NEXT_PUBLIC_API_URL is readable in client code. Remember the rule: never put a secret behind the NEXT_PUBLIC_ prefix.
In server code, read variables with process.env:
export default function Home() {
return (
<main>
<h1>API: {process.env.NEXT_PUBLIC_API_URL}</h1>
</main>
)
}process.env.NEXT_PUBLIC_API_URL is replaced with its value at build time. For production, secret values are injected through the deployment platform — not committed to the repository. .env.example is a safe template to commit as documentation of the variables required.
An important note: NEXT_PUBLIC_ values are frozen at build time. Changing these variables requires a rebuild — injecting new values without rebuilding won't affect production.
Next.js optimizes images automatically through the next/image component. Configuration in next.config.mjs controls the list of allowed domains, output formats like WebP and AVIF, and the default quality. Other static assets go in the public folder and are accessed via the root path, for example the file public/logo.png is available at /logo.png. Full details on media optimization are in episode 9.
Run a build and study its output:
npm run buildThe command npm run build prints a route table: which routes are static, dynamic, and use ISR. Pay attention to the Size column — the JavaScript payload per route. The smaller it is, the faster the page. Starting in episode 15 we'll dissect bundles in depth, but building the habit of reading build output now will develop your performance intuition.
Some practices that keep a project healthy: separate configuration per environment with .env files, never commit secrets, register external image domains explicitly, and verify every new configuration option through the official documentation before using it in production.
Here's what to take away:
In the next episode, episode 9, we'll discuss images and media optimization — the next/image component and image optimization, static assets in the public folder, handling video and audio with lazy loading, and best practices for responsive images and web performance. Your application's media will be optimized properly.