Next.js Environment Variables Not Working
Problem
Environment variables in Next.js can be problematic when they appear as undefined
. This commonly occurs when using the process.env.YOUR_VARIABLE
syntax, even when the variables are properly defined in .env.local
or other environment files.
Root Cause
Next.js handles environment variables differently between server-side and client-side code, and there's an important distinction between build-time and runtime variables.
Key Facts
- Next.js replaces
process.env.*
at build time, not runtime - Environment variables without the
NEXT_PUBLIC_
prefix are only available server-side - Client-side code can only access variables prefixed with
NEXT_PUBLIC_
Solutions
1. Using NEXT_PUBLIC_ Prefix (Recommended for Client-Side)
For variables that need to be accessible in the browser, prefix them with NEXT_PUBLIC_
:
# .env.local
NEXT_PUBLIC_API_URL=http://localhost:3000/api
NEXT_PUBLIC_ANALYTICS_ID=123456789
// In your component
console.log(process.env.NEXT_PUBLIC_API_URL); // Works in browser
Important
Always restart your development server after modifying environment variables:
npm run dev
2. Server-Side Environment Variables
For sensitive data that should not be exposed to the client:
# .env.local
DATABASE_URL=your_database_connection_string
API_SECRET=your_secret_key
// pages/api/data.js (Server-side only)
export default function handler(req, res) {
const dbUrl = process.env.DATABASE_URL; // Only accessible server-side
res.status(200).json({ data: 'secure' });
}
3. Next.config.js Configuration (Legacy Approach)
For Next.js versions prior to 9.4, you can use next.config.js
:
// next.config.js
module.exports = {
env: {
CUSTOM_KEY: process.env.CUSTOM_KEY,
},
};
Deprecation Notice
This approach exposes variables to both server and client, which may not be secure for sensitive data. Use NEXT_PUBLIC_
prefix instead for modern Next.js versions.
4. Runtime Environment Variables
For values that need to change at runtime (not just build time), use a custom solution:
// app/layout.tsx
import { PublicEnvScript } from 'next-runtime-env';
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<PublicEnvScript />
</head>
<body>
{children}
</body>
</html>
);
}
// app/client-page.tsx
'use client';
import { env } from 'next-runtime-env';
export default function SomePage() {
const apiUrl = env('NEXT_PUBLIC_API_URL');
return <div>API URL: {apiUrl}</div>;
}
5. Server-to-Client Prop Passing
For maximum control and security:
// pages/index.js (Server Component)
export default function HomePage({ apiUrl }) {
return <ClientComponent apiUrl={apiUrl} />;
}
export async function getServerSideProps() {
return {
props: {
apiUrl: process.env.API_URL // Pass server env to client as prop
}
};
}
Common Pitfalls and Debugging
Common Mistakes
- Forgetting to restart the dev server after environment changes
- Typos in variable names - use
console.log(process.env)
to debug - Incorrect file location -
.env.local
should be at project root - Using React app conventions -
REACT_APP_
prefix doesn't work in Next.js
// Debug all environment variables
console.log('All env variables:', process.env);
Environment File Hierarchy
Next.js loads environment variables in this order:
.env.local
(for all environments).env.development
/.env.production
(environment-specific).env
(base environment)
Best Practices
- Use
.env.local
for local development (add to.gitignore
) - Use
.env.production
for production-specific variables - Never commit sensitive data to version control
Production Deployment Considerations
When deploying to platforms like Vercel, Heroku, or Docker:
# Set environment variables in your deployment platform
vercel env add NEXT_PUBLIC_API_URL
Production Warning
NEXT_PUBLIC_
variables are embedded at build time. To change them, you must rebuild your application. For truly dynamic runtime variables, use the server-to-client prop pattern or runtime environment solutions.
Version Compatibility
- Next.js 9.4+: Use
NEXT_PUBLIC_
prefix (recommended) - Next.js 9.3 and below: Use
next.config.js
env configuration - Next.js 13+ App Router: All above solutions work, plus new runtime options
By following these patterns, you can reliably manage environment variables in Next.js applications while maintaining security and flexibility across different deployment environments.