Problem Solving8 min read

Mastering Next.js Client-Side Environment Variables in 2026

Effectively managing environment variables in Next.js, especially for client-side consumption within the App Router, presents unique challenges. Many developers struggle with securely exposing necessary configurations without risking sensitive data or breaking builds. This guide provides a production-grade approach for 2026.

KE
Krapton Engineering
Share
Mastering Next.js Client-Side Environment Variables in 2026

As web applications grow in complexity, securely managing configuration — particularly environment variables — becomes paramount. While server-side variables are relatively straightforward, exposing Next.js client-side environment variables in App Router projects often leads to confusion and potential security vulnerabilities. Developers frequently encounter issues where variables are undefined on the client, or worse, sensitive data is accidentally exposed.

TL;DR: For client-side environment variables in Next.js App Router, prefer using NEXT_PUBLIC_ for build-time static values. For dynamic, runtime-dependent client-side values, leverage publicRuntimeConfig in next.config.mjs to ensure secure and flexible configuration without exposing secrets or requiring full rebuilds for minor changes.

The Client-Side Conundrum: Why Next.js Env Vars Are Tricky

Detailed view of code and file structure in a software development environment.
Photo by Daniil Komov on Pexels

In traditional Node.js applications, process.env is a global object accessible only on the server. Next.js, a full-stack framework, blurs this line by supporting both server-side rendering (SSR), client-side rendering (CSR), and more recently, React Server Components (RSC) within the App Router. This architectural flexibility, while powerful, introduces a nuanced challenge for environment variable management.

A common naive approach is to define all variables in .env.local and expect them to be available everywhere. However, Next.js implements strict rules. Any variable intended for the browser must be prefixed with NEXT_PUBLIC_. Without this, Next.js will tree-shake it out of the client-side bundle, leading to undefined errors. While this prevents accidental leakage of server-only secrets, it often catches developers off guard.

In a recent client engagement, we observed a team struggling with this exact issue during an App Router migration. They had moved several components to client-side boundaries (`'use client'`) but forgot to prefix their feature flag environment variables. The result was a broken UI only in production, as the variables were correctly picked up during local development (where the server might still render initial components) but disappeared after client-side hydration. This highlighted the critical distinction between build-time and runtime environments in a Next.js deployment.

Understanding Next.js Environment Variable Resolution in 2026

Close-up of colorful programming code displayed on a monitor screen.
Photo by Myburgh Roux on Pexels

Next.js processes environment variables at different stages:

  • Build Time: Variables prefixed with NEXT_PUBLIC_ are inlined into the client-side JavaScript bundle during the build process. Changes to these variables require a full rebuild and redeploy. Non-prefixed variables are only available on the server.
  • Runtime (Server-side): Non-prefixed variables (e.g., database connection strings, API keys) are accessible within Node.js environments (API routes, server components, server-side rendering functions). These can be dynamically loaded at server startup.
  • Runtime (Client-side): Direct access to process.env for non-NEXT_PUBLIC_ variables is impossible. For truly dynamic client-side variables that can change without a rebuild (e.g., feature toggles controlled by an admin panel), a different approach is needed.

The App Router, introduced in Next.js 13 and refined in Next.js 15.2, further emphasizes this separation. Server Components, by default, run on the server and have access to all environment variables. Client Components, however, are restricted to NEXT_PUBLIC_ variables or data passed down from Server Components or a dedicated runtime configuration. You can learn more about Next.js environment variable specifics in their official documentation.

Production-Grade Strategy: Securely Exposing Client-Side Env Vars

For production-ready Next.js applications, especially those leveraging the App Router, a robust strategy for Next.js environment variables client-side involves a combination of NEXT_PUBLIC_ for static, public variables and a custom runtime configuration for dynamic, non-sensitive client-side values.

1. Static Public Variables (Build-Time)

Use NEXT_PUBLIC_ for variables that are not sensitive and do not change frequently, such as public API URLs or application names. These are baked into the client bundle.

# .env.local
NEXT_PUBLIC_API_BASE_URL=https://api.example.com/v1
NEXT_PUBLIC_APP_NAME="Krapton App"
// In a client component or page
const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL;
console.log(apiBaseUrl); // https://api.example.com/v1

2. Dynamic Public Variables (Runtime) via publicRuntimeConfig

For client-side variables that need to be dynamic (e.g., changed without a full rebuild) but are still non-sensitive, Next.js offers publicRuntimeConfig via next.config.mjs. This is particularly useful for feature flags, CDN URLs, or other configuration that might vary between deployment environments or be updated post-build.

On a production rollout we shipped, the failure mode was related to A/B test variations tied to environment variables. Using NEXT_PUBLIC_ meant every A/B change required a full CI/CD pipeline run. By switching to publicRuntimeConfig and pulling values from a lightweight API endpoint, we decoupled feature flag updates from the deployment cycle, significantly improving agility.

// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  experimental: {
    serverActions: true, // Example for App Router
  },
  publicRuntimeConfig: {
    // Will be available on both server and client
    // Ensure these are NOT sensitive values!
    featureFlagBeta: process.env.FEATURE_FLAG_BETA || 'false',
    cdnHost: process.env.CDN_HOST || 'https://cdn.default.com',
  },
  serverRuntimeConfig: {
    // Will only be available on the server
    mySecretKey: process.env.SECRET_KEY,
  },
};

export default nextConfig;

To access these variables, you'll use getConfig() from next/config:

// In a client component or page (e.g., src/app/layout.tsx or a 'use client' component)
import getConfig from 'next/config';

const { publicRuntimeConfig } = getConfig();
const { featureFlagBeta, cdnHost } = publicRuntimeConfig;

if (featureFlagBeta === 'true') {
  console.log('Beta features are enabled!');
}
console.log('CDN Host:', cdnHost);

This method ensures that these variables are injected at runtime, making them flexible. However, remember they are still publicly visible in the client bundle, so they must not contain sensitive information.

When NOT to use this approach

While publicRuntimeConfig offers flexibility, it's crucial to understand its limitations. Do NOT use this for highly sensitive data like API keys, database credentials, or private access tokens. Any value exposed via publicRuntimeConfig is accessible in the client's browser, making it vulnerable to inspection. For such secrets, always keep them server-side and access them via secure API endpoints if needed by the client, or pass them as props from a Server Component.

Handling Sensitive Server-Side Variables

For truly sensitive data, ensure variables remain exclusively on the server. This means:

  • No NEXT_PUBLIC_ prefix.
  • Store in .env.local (for development) or secure platforms like Vercel's Environment Variables, AWS Secrets Manager, or Kubernetes Secrets (for production).
  • Access only in Server Components, API Routes, or getServerSideProps/getStaticProps (if using Pages Router).

For instance, a database connection string or a third-party API secret should never leave your server environment. If your client-side code needs to interact with a service requiring a secret, the client should make a request to your own API endpoint, which then securely handles the interaction with the third-party service using the server-side secret. Krapton offers software security services to help architect these secure patterns.

For deployment on platforms like Vercel, their Environment Variables management provides a secure way to inject server-side secrets into your Next.js builds and runtimes.

Common Pitfalls and How to Avoid Them

  • Forgetting NEXT_PUBLIC_: The most common error. If a variable is undefined on the client, check its prefix.
  • Leaking Secrets via API Routes: Be careful not to inadvertently return server-side environment variables in API route responses. Always filter or sanitize data.
  • Stale Environment Variables in Cached Builds: If you change a NEXT_PUBLIC_ variable, remember it requires a full rebuild. If not, your users might be served an old cached version. For runtime variables, ensure your deployment pipeline correctly propagates updates to publicRuntimeConfig.
  • Over-reliance on process.env in App Router: While `process.env` works, understand where it works. Server Components can use all variables, but client components are restricted. Explicitly passing data down or using publicRuntimeConfig for client-side needs is clearer.

Navigating these complexities requires deep understanding of Next.js internals and deployment best practices. If your team is facing persistent issues, consider partnering with experts. Hire Next.js developers from Krapton to streamline your development process.

FAQ: Next.js Environment Variables

How do I access .env variables in a Next.js client component?

To access environment variables directly in a client component, they must be prefixed with NEXT_PUBLIC_ (e.g., NEXT_PUBLIC_MY_VAR). These variables are embedded into the client-side JavaScript bundle during the build process and can be accessed via process.env.NEXT_PUBLIC_MY_VAR.

What's the difference between build-time and runtime environment variables in Next.js?

Build-time variables are static values injected into the code during compilation (e.g., NEXT_PUBLIC_ variables). Runtime variables are resolved when the application starts or a request is made. Next.js server-side variables are runtime, and publicRuntimeConfig provides a way to have client-accessible runtime variables.

Can I use environment variables with React Server Components (RSC)?

Yes, React Server Components (RSC) run on the server, so they have full access to all environment variables, including those without the NEXT_PUBLIC_ prefix. This makes them ideal for fetching sensitive data or configuring server-side logic securely.

Why are my Next.js environment variables undefined on Vercel?

This usually happens if you haven't correctly configured your environment variables in Vercel's project settings for the specific environment (e.g., Production, Preview, Development). Ensure variables are added and, if client-side, are prefixed with NEXT_PUBLIC_. Also, remember to redeploy after adding new variables.

Is it safe to expose API keys using NEXT_PUBLIC_ in Next.js?

No, it is generally not safe to expose any sensitive API keys using NEXT_PUBLIC_. These variables are bundled into the client-side JavaScript and are visible to anyone inspecting the page source. Only use NEXT_PUBLIC_ for non-sensitive, public configurations.

Need Expert Next.js Development?

Managing environment variables, especially across complex Next.js App Router architectures, can be a bottleneck for development teams. If you need robust, secure, and scalable solutions for your web applications, Krapton’s senior engineers are ready to help. Our team specializes in building and optimizing Next.js projects, ensuring best practices for security and performance. Don't let environment configuration slow down your next big launch.

Book a free consultation with Krapton today and let us help you ship production-grade Next.js applications.

About the author

Krapton Engineering has extensive experience shipping complex Next.js applications for startups and enterprises globally, tackling challenges from App Router migrations to optimizing performance and securing environment configurations at scale for over a decade.

Tagged:javascriptreactnextjsdebuggingperformancetutorialhow-toenvironment variablesapp router
Work with us

Ready to Build with Us?

Our senior engineers are available for your next project. Start in 24 hours.