React Environment Variables (.env)

React Environment Variables (.env)

Environment variables are a foundational concept in software development, allowing developers to configure applications across multiple environments—development, staging, production—without changing the source code. In React applications, environment variables provide a secure and flexible way to manage configuration values, such as API endpoints, feature flags, and third‑party service credentials.

This article offers a comprehensive dive into using environment variables within React projects. Whether you’re using Create React App (CRA), a custom Webpack setup, or a modern bundler like Vite, you’ll learn how to:

  • Define and access environment variables
  • Manage multiple sets of variables for different stages
  • Secure sensitive information
  • Apply best practices to avoid common traps

By the end, you’ll be equipped to handle complex configuration scenarios in any React-based workflow.

What Are Environment Variables?

Environment variables are dynamic key-value pairs set outside your application code, typically at the operating system or process level. They enable:

  • Decoupling Configuration from Code
  • Easier Deployment across different servers or services
  • Enhanced Security by keeping secrets out of your code repository

When your React app starts (or during its build process), these variables become available to the application logic, influencing behavior without code changes.

Why Use Environment Variables in React?

React is primarily a client‑side library. By default, code and assets are bundled and shipped to users’ browsers. This presents several challenges:

  1. Configuration Differences
    • Development vs Production URLs
    • Feature toggles enabled only in testing
  2. Security
    • Avoid exposing secrets in your repository
    • Limit which values truly travel to the browser
  3. Maintainability
    • One codebase, many deployments
    • Clear separation of concerns

Environment variables provide the solution: you supply the right configuration at build or runtime, and the application adapts automatically.

Setting Up Environment Variables with Create React App

Create React App (CRA) offers built‑in support for environment variables, enforcing a simple convention to balance ease of use with security.

The REACT_APP_ Prefix

By default, CRA will only expose variables prefixed with REACT_APP_. For example:

# .env.development
REACT_APP_API_URL=https://dev.api.example.com
REACT_APP_FEATURE_FLAG=true

Variables without this prefix will be ignored, preventing accidental leakage of sensitive data.

Available .env Files

CRA recognizes multiple .env files located at the project root:

  • .env
  • .env.local
  • .env.development, .env.production, .env.test
  • .env.development.local, .env.production.local, .env.test.local


File Description
| .env            | Default environment variables for all environments
| .env.local  | Local overrides (ignored by Git by default)
| .env.development  | Overrides for development mode (npm start)
| .env.development.local  | Local overrides for development
| .env.test  | Overrides for test mode (npm test)
| .env.test.local  | Local overrides for test
| .env.production  | Overrides for production mode (npm run build)
| .env.production.local  | Local overrides for production

Tip: Always include .env.local in your .gitignore to ensure secrets never enter version control.

Loading Order and Overrides

CRA loads these files in a specific order, where later files override earlier ones:

  1. .env
  2. .env.local
  3. .env.{NODE_ENV}
  4. .env.{NODE_ENV}.local

For instance, during development (NODE_ENV=development), values in .env.development.local take precedence over those in .env.development, which in turn override .env.local, which override .env.

Accessing Environment Variables in Code

Once defined, environment variables are accessed in your React code via process.env:

// src/config.js
export const API_URL = process.env.REACT_APP_API_URL;
export const IS_FEATURE_ENABLED = process.env.REACT_APP_FEATURE_FLAG === 'true';
Note: All values from .env files are strings. Convert them explicitly (e.g., with JSON.parse, Number(), or strict comparisons).

You can also inline them directly:

function App() {
  return (
    <div>
      <h1>Welcome!</h1>
      <p>API endpoint: {process.env.REACT_APP_API_URL}</p>
    </div>
  );
}

However, centralizing access in a single module (e.g., config.js) helps manage conversions and defaults elegantly.

Environment Variables with Custom Webpack Setups

Not using CRA? You can still leverage environment variables with custom Webpack configurations.

Defining Variables via DefinePlugin

Webpack’s DefinePlugin lets you create compile‑time constants:

// webpack.config.js
const webpack = require('webpack');
const dotenv = require('dotenv');

// Load .env
const env = dotenv.config().parsed;

// Convert keys to webpack-friendly format
const envKeys = Object.keys(env).reduce((prev, next) => {
  prev[`process.env.${next}`] = JSON.stringify(env[next]);
  return prev;
}, {});

module.exports = {
  // ...other config...
  plugins: [
    new webpack.DefinePlugin(envKeys)
  ],
};

This approach injects each variable into your bundle, replacing process.env.MY_VAR with the literal value at build time.

Using Third‑Party Libraries (dotenv)

When not using CRA, you can install dotenv to load .env files:

npm install dotenv --save

Then, at the top of your entry point (e.g., src/index.js):

import dotenv from 'dotenv';
dotenv.config();

// Now process.env.MY_CUSTOM_VAR is available...

Be mindful: dotenv.config() runs at runtime in your Node environment, but the variables must still be injected into your client code via Webpack’s DefinePlugin (or similar).

Using Environment Variables in Vite

Vite has its own conventions. By default, it exposes variables prefixed with VITE_.

Naming Conventions (VITE_ Prefix)

# .env
VITE_API_URL=https://api.vite.example.com
VITE_ENABLE_LOGS=false

Within your code:

// vite.config.js
export default defineConfig({
  // ...
});

// src/main.jsx console.log(import.meta.env.VITE_API_URL);
Note: Vite’s import.meta.env differs from the Webpack standard. Only keys with VITE_ are exposed.

.env File Hierarchy

Vite loads these files by default:

  • .env
  • .env.local
  • .env.[mode]
  • .env.[mode].local

Where [mode] corresponds to the current mode (e.g., development, production). The loading order mirrors CRA’s pattern.

Managing Multiple Environments

Complex projects often require more than just development and production.

Development vs Production vs Test

  • Development: Fast refresh, verbose logging
  • Production: Minification, optimized assets
  • Test: Mocked services, code coverage

Use separate .env files, scripts in package.json, or CI/CD pipelines to select the correct environment.

// package.json
"scripts": {
  "start": "react-scripts start",
  "build:staging": "env-cmd -f .env.staging react-scripts build",
  "build:prod": "react-scripts build"
}

Tools like env-cmd or cross-env enable more granular control.

Staging, QA, Canary Environments

For additional tiers:

  1. Create .env.staging, .env.qa, .env.canary
  2. Use CI variables or command-line flags to select the target
# CI example (GitHub Actions)
- name: Build Staging
  run: |
    cp .env.staging .env
    npm run build

Tips for Switching Environments

  • Automate the copy/selection process in CI/CD
  • Document required variables in a .env.example
  • Validate missing keys at build time using scripts

Security Considerations

Because React is client‑side, anything you include in your bundle becomes public. Keep this in mind when deciding what to place in environment variables.

What Should Not Go in Environment Variables

  • Long‑lived API secrets (e.g., database passwords)
  • OAuth client secrets
  • Cryptographic keys

Protecting API Keys and Secrets

  • Move secrets to a backend service or proxy
  • Use short‑lived tokens, rotating them frequently
  • Restrict origins and scopes on third‑party services

Server‑Side vs Client‑Side Variables

  • Server‑only: Store invisibly in your backend; never inject into client
  • Client‑visible: Only share non‑sensitive, short‑lived configs

Best Practices & Common Pitfalls

Avoid Committing Secrets

  • Add .env.local to .gitignore
  • Use a template .env.example to list keys without values

Documenting Variables

Create ENVIRONMENT.md:

# Environment Variables

## Required

- `REACT_APP_API_URL` – Base URL for API requests  
- `REACT_APP_FEATURE_FLAG` – Toggle new UI  

## Optional

- `REACT_APP_LOG_LEVEL` – `info` / `debug`

Consistent Naming

  • Use uppercase and _ separators
  • Adopt clear prefixes (e.g., REACT_APP_, VITE_)

Fallbacks and Defaults

Always supply defaults for missing values:

export const API_URL = process.env.REACT_APP_API_URL || 'https://fallback.example.com';

This prevents runtime undefined errors.

Advanced Tips & Tricks

TypeScript and Environment Variables

Augment types for process.env:

// src/react-app-env.d.ts
declare namespace NodeJS {
  interface ProcessEnv {
    REACT_APP_API_URL: string;
    REACT_APP_FEATURE_FLAG: 'true' | 'false';
  }
}

This ensures compile‑time checks on process.env.

Injecting at Build Time vs Runtime

By default, CRA injects at build time. For runtime injection:

  1. Serve a config.json alongside your build
  2. Fetch it in index.html before React mounts
  3. Store values on window.__APP_CONFIG__

This permits changing configs without rebuilding.

Using .env.local Securely

  • Keep machine‑specific overrides out of VCS
  • Share secrets via secure channels (e.g., Vault)
  • Rotate and revoke when devices change hands

Debugging Environment Variable Issues

  1. Missing Prefix: Ensure REACT_APP_ or VITE_ is present.
  2. Wrong File Location: Place .env at the project root.
  3. Caching: Restart dev server after changing .env.
  4. Type Conversions: Remember all values are strings.

Use console.log(process.env) early in App.js to inspect loaded keys.

Real‑World Use Cases

  • Feature Toggles: Turn features on/off without redeploying
  • Multitenancy: Select tenant API endpoints dynamically
  • A/B Testing: Drive experiments via env vars in CI
  • Third‑Party Integrations: Configure analytics, maps, payment gateways

In each scenario, env vars decouple code from context, reducing complexity.

Conclusion

Environment variables are indispensable to modern React development. They:

  • Simplify configuration management
  • Enhance security by segregating secrets
  • Enable flexible deployments across environments

Whether you use Create React App, Webpack, or Vite, understanding naming conventions, loading order, and best practices will help you ship robust, maintainable React applications. Remember to never expose long‑lived secrets in client bundles and to document every variable clearly. With these guidelines, you can confidently manage your React app’s configuration and scale it across any environment.

#React #environment variables #Create React App #CRA #Vite #Webpack #dotenv #configuration #feature flags #API endpoints #security best practices #runtime variables #build-time variables

Subscribe to our newsletter

12k+

Subscribers

Weekly

Frequency

Free

Always