env-doctor has first-class support for Next.js applications.
env-doctor automatically detects Next.js projects by:
next.config.js, next.config.mjs, or next.config.tsnext in package.json dependenciesNext.js uses the NEXT_PUBLIC_ prefix to expose variables to the browser:
# Server-only (never sent to browser)
DATABASE_URL=postgres://...
API_SECRET=secret123
# Client-accessible (bundled into JavaScript)
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_GA_ID=UA-12345678
env-doctor warns when:
NEXT_PUBLIC_ variablesNEXT_PUBLIC_ prefix (security risk)⚠ Framework Convention Warning
DATABASE_URL
Variable is used in client component but doesn't have NEXT_PUBLIC_ prefix
at app/components/DataView.tsx:5
Next.js loads env files in this order:
| File | Loaded When | Commit? |
|---|---|---|
.env |
Always | Yes |
.env.local |
Always (except test) | No |
.env.development |
next dev |
Yes |
.env.development.local |
next dev |
No |
.env.production |
next build |
Yes |
.env.production.local |
next build |
No |
.env.test |
NODE_ENV=test |
Yes |
.env.test.local |
NODE_ENV=test |
No |
// env-doctor.config.js
module.exports = {
envFiles: [
'.env',
'.env.local',
'.env.development',
'.env.development.local'
],
templateFile: '.env.example',
framework: 'nextjs'
};
Or target specific environment:
npx env-doctor --env production
These Next.js/Vercel variables are automatically ignored:
NEXT_TELEMETRY_DISABLEDNEXT_RUNTIMEVERCELVERCEL_ENVVERCEL_URLVERCEL_REGION// app/page.tsx (Server Component)
export default function Page() {
// ✅ Server-only - works fine
const dbUrl = process.env.DATABASE_URL;
return <div>...</div>;
}
// app/components/ClientComponent.tsx
'use client';
export default function ClientComponent() {
// ✅ Correct - uses NEXT_PUBLIC_
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
// ❌ Warning - won't work in browser
const secret = process.env.API_SECRET;
return <div>...</div>;
}
// pages/index.tsx
export default function Page({ data }) {
// ✅ Correct - client-side access
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
return <div>...</div>;
}
export async function getServerSideProps() {
// ✅ Server-only - works fine
const dbUrl = process.env.DATABASE_URL;
return { props: { data: await fetchData(dbUrl) } };
}
Problem: Variable not available in browser.
// ❌ Won't work
const apiUrl = process.env.API_URL;
Fix: Add the prefix.
# .env
NEXT_PUBLIC_API_URL=https://api.example.com
// ✅ Works
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
Problem: Secret accessible in browser.
# ❌ Security risk
NEXT_PUBLIC_DATABASE_URL=postgres://user:pass@host/db
Fix: Remove prefix, access server-side only.
# ✅ Safe
DATABASE_URL=postgres://user:pass@host/db
Next.js inlines NEXT_PUBLIC_ values at build time. For runtime configuration:
// next.config.js
module.exports = {
publicRuntimeConfig: {
apiUrl: process.env.API_URL
}
};
// Usage
import getConfig from 'next/config';
const { publicRuntimeConfig } = getConfig();
const apiUrl = publicRuntimeConfig.apiUrl;
// env-doctor.config.js
module.exports = {
envFiles: ['.env', '.env.local'],
templateFile: '.env.example',
include: [
'app/**/*.{ts,tsx}',
'pages/**/*.{ts,tsx}',
'src/**/*.{ts,tsx}',
'lib/**/*.{ts,tsx}'
],
exclude: [
'node_modules',
'.next',
'**/*.test.*'
],
framework: 'nextjs',
variables: {
DATABASE_URL: {
required: true,
secret: true,
pattern: /^postgres:\/\//
},
NEXT_PUBLIC_API_URL: {
required: true,
type: 'url'
}
}
};