How .NET and React Apps Read Environment Variables
This is the single biggest mental-model gap for backend developers moving into
frontend deployment. Read this page carefully before touching a .env file.
.NET reads its configuration while running. React/Vite reads its configuration
while being built, and never again. Editing a .env file on a server after
a React app is already deployed there does nothing until you rebuild and redeploy.
Section A — .NET: Runtime Configuration
ASP.NET Core applications like Flow read configuration through
IConfiguration, a layered abstraction that merges values from multiple sources —
appsettings.json, an environment-specific overlay file, actual OS environment variables,
command-line arguments, and (optionally) secret stores — with later sources overriding earlier ones.
Individual values can also be read directly with
Environment.GetEnvironmentVariable("SOME_KEY").
Crucially, this happens every time the process starts, and for some configuration
providers, it can even be reloaded while the process keeps running (ASP.NET Core's
JSON configuration provider supports reloadOnChange, so certain settings can pick up file
edits without a restart at all). At minimum, changing an environment variable and restarting the
process is enough — there is no separate "build" step standing between the config file and the running
application.
Conceptual example — Program.cs// IConfiguration is populated from layered sources at startup
var builder = WebApplication.CreateBuilder(args);
string? connectionString = builder.Configuration["Database:ConnectionString"];
string? apiKey = Environment.GetEnvironmentVariable("SOME_API_KEY");
// values are live for the lifetime of this process
The practical consequence: to change how Flow behaves in an environment, you edit configuration and restart the process. You do not need to touch the compiled DLLs.
Section B — React / Vite: Build-Time Configuration ONLY
React apps built with Vite (like FlowStudio) read environment variables through
import.meta.env.VITE_*. Only variables prefixed with VITE_ are exposed to
client code — this is a deliberate Vite security boundary that prevents accidentally shipping unrelated
server secrets into the browser bundle.
FlowStudio source — main.tsxconst API_BASE_URL = calculateApiUrl(import.meta.env.VITE_API_BASE_URL);
const TENANT_ID = parseInt(import.meta.env.VITE_TENANT_ID ?? '1', 10);
The critical difference from .NET: import.meta.env.VITE_API_BASE_URL is not a live lookup
against the environment at the moment the code runs. Vite's build process performs a
static text substitution — every occurrence of import.meta.env.VITE_API_BASE_URL
in the source is literally replaced with the string value that was present in the relevant
.env file when vite build ran. The compiled JavaScript in
dist/ contains that literal string, not a variable lookup.
There is no window.APP_CONFIG global that a deployed FlowStudio bundle reads at
page-load time, and no mechanism that watches NODE_ENV on the server and swaps API
URLs automatically at runtime. Once a bundle is built, its baked-in values are fixed — full stop.
(There is one legitimate runtime computation, calculateApiUrl(), which derives a URL
from window.location.origin in the browser — but that's different from re-reading
environment variables; see Chapter 2.4 for the full explanation.)
The practical consequence: to change how FlowStudio behaves in an environment, you must edit the
relevant .env file and run npm run build (or the project's equivalent) again,
then redeploy the newly generated dist/ folder. Restarting a web server that merely serves
the old static files changes nothing.
Side-by-Side Summary
| .NET (Flow) | React / Vite (FlowStudio) | |
|---|---|---|
| API surface | IConfiguration, Environment.GetEnvironmentVariable |
import.meta.env.VITE_* |
| Resolved when? | Process startup / live reload | vite build time only |
| To apply a change | Edit config, restart process | Edit .env, rebuild, redeploy |
| Where the value lives after "loading" | In process memory, reloadable | Baked as literal text into shipped JS |
| Runtime re-reading of env vars in the browser? | N/A | Not implemented — no window.APP_CONFIG |
The Diagram
.NET's loop is short — edit, restart. React/Vite's loop always includes a build step in between.