1. Introduction ๐
Every real application needs a way to change behavior between development, preview, and production without changing code. Next.js handles this through environment variables and the next.config.ts file. This tutorial covers both โ how to load secrets safely, and how to shape the framework's build and runtime behavior.
Information
2. What are Environment Variables? ๐ค
An environment variable is a named value available to your running code but stored outside of it โ in a file or your hosting provider's dashboard โ so the same codebase can behave differently across environments.
- Database connection strings, API keys, feature flags.
- Accessed in code via process.env.VARIABLE_NAME.
- Never hardcoded directly into source files.
3. Why Use Environment Variables? ๐ฏ
Beyond convenience, environment variables keep secrets out of source control and let the exact same build artifact be deployed to different environments with different configuration.
Tip
4. Environment Files ๐
Next.js automatically loads variables from several .env file variants, each serving a different purpose.
| File | Loaded when | Should be committed? |
|---|---|---|
| .env | Always | Yes (no secrets) |
| .env.local | Always (except test) | Never |
| .env.development | next dev | Yes (no secrets) |
| .env.production | next build / next start | Yes (no secrets) |
5. Loading Environment Variables ๐ฅ
Next.js loads these files automatically โ no extra package like dotenv is required. Values become available on process.env as soon as the process starts.
.env.local
DATABASE_URL="postgresql://user:pass@localhost:5432/mydb"
STRIPE_SECRET_KEY="sk_live_xxx"
NEXT_PUBLIC_ANALYTICS_ID="G-XXXXXXX"6. Public Environment Variables ๐
Any variable prefixed with NEXT_PUBLIC_ is inlined into the client-side JavaScript bundle at build time, making it accessible in the browser.
components/Analytics.tsx
'use client';
export function Analytics() {
const id = process.env.NEXT_PUBLIC_ANALYTICS_ID;
return <script data-analytics-id={id} />;
}Warning
7. Server-Only Environment Variables ๐
Variables without the NEXT_PUBLIC_ prefix are only available in server-side code โ Server Components, Route Handlers, Middleware, and Server Actions โ and are stripped from the client bundle.
app/api/checkout/route.ts
export async function POST(request: Request) {
const stripeKey = process.env.STRIPE_SECRET_KEY; // safe: server-only
const client = new Stripe(stripeKey!);
// ...
}8. process.env ๐งพ
process.env is the standard Node.js object through which all environment variables are read, regardless of framework. Next.js replaces references at build time for statically-known keys, enabling dead-code elimination.
lib/config.ts
export const isProduction = process.env.NODE_ENV === 'production';
export const apiUrl = process.env.API_URL ?? 'http://localhost:3000';Caution
9. Environment Variable Expansion ๐
Next.js supports referencing one variable's value from another using $VARIABLE_NAME syntax within a .env file.
.env
BASE_URL="https://api.example.com"
USERS_ENDPOINT="$BASE_URL/users"10. Environment Variable Load Order ๐
When the same variable is defined in multiple files, Next.js resolves it using a fixed precedence order, checking each file in turn until a value is found.
- process.env (already set in the shell/host)
- .env.$(NODE_ENV).local
- .env.local (skipped in test)
- .env.$(NODE_ENV)
- .env
11. Development Configuration ๐ ๏ธ
Use .env.development (or .env.local) for values specific to local development, like a local database URL or a test-mode payment key.
.env.development
DATABASE_URL="postgresql://localhost:5432/dev_db"
STRIPE_SECRET_KEY="sk_test_xxx"12. Production Configuration ๐ญ
Production secrets are almost always set through your hosting provider's dashboard (Vercel, for example) rather than committed to .env.production, since that file is version-controlled.
Best Practice
13. Preview Configuration ๐๏ธ
Many hosting providers support a separate preview environment for pull requests, letting you use a staging database or sandboxed API keys distinct from both development and production.
| Environment | Typical Use |
|---|---|
| Development | Local machine, hot reload |
| Preview | Pull request deployments, staging data |
| Production | Live traffic, real secrets |
14. next.config.ts โ๏ธ
next.config.ts (or .js) is where you configure framework-level behavior โ build output, redirects, image domains, experimental flags โ as opposed to runtime secrets.
next.config.ts
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
reactStrictMode: true,
images: { domains: ['cdn.example.com'] },
};
export default nextConfig;15. Application Configuration ๐งฉ
Beyond next.config.ts, most apps keep a small, typed configuration module that centralizes reads from process.env, so the rest of the codebase never touches process.env directly.
lib/config.ts
export const config = {
databaseUrl: process.env.DATABASE_URL!,
stripeKey: process.env.STRIPE_SECRET_KEY!,
siteUrl: process.env.NEXT_PUBLIC_SITE_URL ?? 'http://localhost:3000',
};16. Runtime Configuration ๐
Unlike build-time inlined NEXT_PUBLIC_ variables, true runtime configuration is read fresh every time the server process starts, letting the same build be deployed to multiple environments unchanged.
Information
17. Build Configuration ๐๏ธ
Some next.config.ts options directly affect the build process itself โ like output mode, TypeScript/ESLint error handling, or compiler options.
next.config.ts
const nextConfig: NextConfig = {
typescript: { ignoreBuildErrors: false },
eslint: { ignoreDuringBuilds: false },
};18. Experimental Features ๐งช
The experimental key in next.config.ts gates in-progress features that may change or be removed between releases โ useful for early adoption but riskier for production stability.
next.config.ts
const nextConfig: NextConfig = {
experimental: {
ppr: true,
typedRoutes: true,
},
};Caution
19. Turbopack Configuration โก
Turbopack is Next.js's Rust-based bundler, configurable through the turbopack key for custom loaders, resolve aliases, and module rules.
next.config.ts
const nextConfig: NextConfig = {
turbopack: {
resolveAlias: {
'@ui': './src/components/ui',
},
},
};20. Webpack Configuration ๐ฆ
For projects not yet on Turbopack, the webpack() function lets you extend or override the underlying webpack configuration directly.
next.config.ts
const nextConfig: NextConfig = {
webpack(config) {
config.resolve.alias['@ui'] = './src/components/ui';
return config;
},
};21. Redirects Configuration โช๏ธ
The redirects() function lets you define permanent or temporary URL redirects centrally, without writing Middleware or per-page logic.
next.config.ts
const nextConfig: NextConfig = {
async redirects() {
return [
{ source: '/old-blog/:slug', destination: '/blog/:slug', permanent: true },
];
},
};22. Rewrites Configuration ๐
The rewrites() function maps an incoming path to a different destination without changing the URL the visitor sees โ useful for proxying an external API.
next.config.ts
const nextConfig: NextConfig = {
async rewrites() {
return [
{ source: '/api/proxy/:path*', destination: 'https://external-api.com/:path*' },
];
},
};23. Headers Configuration ๐ท๏ธ
The headers() function attaches custom response headers to matched paths at the framework level, ideal for global security or caching headers.
next.config.ts
const nextConfig: NextConfig = {
async headers() {
return [
{
source: '/(.*)',
headers: [{ key: 'X-Frame-Options', value: 'DENY' }],
},
];
},
};24. Image Configuration ๐ผ๏ธ
The images key configures which external domains the built-in next/image component is allowed to optimize, along with formats and device sizes.
next.config.ts
const nextConfig: NextConfig = {
images: {
remotePatterns: [{ protocol: 'https', hostname: 'cdn.example.com' }],
formats: ['image/avif', 'image/webp'],
},
};25. Transpile Packages ๐ฆ
The transpilePackages option tells Next.js to compile specific node_modules packages through its own build pipeline โ necessary for monorepo packages or libraries shipped as untranspiled ESM/TS.
next.config.ts
const nextConfig: NextConfig = {
transpilePackages: ['ui', '@my-org/shared-components'],
};26. Output Configuration ๐ค
The output option changes what next build produces โ the default full server build, a minimal standalone bundle for containers, or a fully exported static site.
next.config.ts
const nextConfig: NextConfig = {
output: 'standalone', // ideal for Docker deployments
};| Value | Use Case |
|---|---|
| (default) | Standard Node.js server deployment |
| standalone | Minimal, self-contained Docker image |
| export | Fully static site, no server required |
27. Security Best Practices ๐
- Never prefix secrets with NEXT_PUBLIC_ โ that inlines them into publicly readable JavaScript.
- Add .env*.local to .gitignore so local secrets never reach version control.
- Store production secrets in your hosting provider's encrypted environment variable manager, not in a committed file.
- Rotate leaked keys immediately and audit git history if a secret was ever committed by mistake.
Danger
28. Common Configuration Mistakes ๐ซ
29. Frequently Asked Questions โ
Yes โ environment files are only read when the Next.js process starts, so any change to a .env file requires restarting next dev.
Yes โ recent versions of Next.js support next.config.ts directly, giving you full type-checking on configuration options.
No โ Next.js sets NODE_ENV automatically based on the command you run (development for next dev, production for next build/next start); you shouldn't override it manually.