This site is not affiliated with or endorsed by Vercel, Inc. Read the docs, then deploy or run each experiment yourself.
Platform

Environment Variable Inspector

Use dummy values to learn which variables are public, server-only, or invalid.

Run this experiment yourself

Demos are not embedded on this site. Deploy a standalone copy on Vercel or run the experiment app locally.

Local development

cd apps/experiments/environment-variable-inspector
pnpm install
pnpm dev

Then open http://localhost:3010.

This is an experimental demo. Use it as a starting point for your own projects.

The Environment Variable Inspector is a client-only experiment where you edit a dummy .env sample and compare two views of the same data: what the server runtime would see and what a client bundle would expose. It is built with React state and four pure functions in apps/experiments/environment-variable-inspector/logic.ts. It has no API routes, no external services, and needs no environment variables of its own.

It teaches the rule at the heart of Next.js configuration: only variables prefixed with NEXT_PUBLIC_ are inlined into browser code, while everything else stays on the server.

The editor starts with hardcoded dummy values and the page never reads your real process.env or any .env file. Do not paste real secrets into it. The text you type is processed in your browser only, and is not sent anywhere by this experiment.

Features

  • Editable dummy .env text area seeded with a sample.
  • Line-by-line parsing with comments, blank lines, and quoted values.
  • Parse errors shown with line numbers.
  • Client bundle view: public values shown, server values replaced with [REDACTED - server only].
  • Server runtime view: every valid key with its value.
  • Reset button restoring the sample.

UI Reference

This experiment has no API routes.

Prop

Type

Classification rules

classifyEnvKey trims the key and applies these rules in order:

ConditionClassificationClient viewServer view
Key does not match ^[A-Z][A-Z0-9_]*$ (empty, lowercase, starts with a digit, contains -, spaces, etc.)invalidomittedomitted
Key starts with NEXT_PUBLIC_publicvalue shownvalue shown
Any other valid keyserver[REDACTED - server only]value shown

Default sample

# Dummy values for education only
NEXT_PUBLIC_SITE_URL=https://demo.example.com
NEXT_PUBLIC_FEATURE_FLAG=true
DATABASE_URL=postgres://demo:demo@localhost:5432/demo
API_SECRET_KEY=sk_demo_not_real

Client bundle view for that sample:

{
  "NEXT_PUBLIC_SITE_URL": "https://demo.example.com",
  "NEXT_PUBLIC_FEATURE_FLAG": "true",
  "DATABASE_URL": "[REDACTED - server only]",
  "API_SECRET_KEY": "[REDACTED - server only]"
}

Parse errors look like this (for a line with no =):

Line 1: expected KEY=value

Implementation Details

Flow

Parse the text

parseEnvFile splits on \n or \r\n, skips blanks and # comments, requires an = after at least one character, trims the key and value, and strips one pair of matching quotes.

lines.forEach((raw, index) => {
  const line = index + 1;
  const trimmed = raw.trim();
  if (!trimmed || trimmed.startsWith('#')) return;

  const eq = trimmed.indexOf('=');
  if (eq <= 0) {
    errors.push(`Line ${line}: expected KEY=value`);
    return;
  }

  const key = trimmed.slice(0, eq).trim();
  let value = trimmed.slice(eq + 1).trim();
  if (
    (value.startsWith('"') && value.endsWith('"')) ||
    (value.startsWith("'") && value.endsWith("'"))
  ) {
    value = value.slice(1, -1);
  }

  entries.push({ key, value, line });
});

Classify each key

const PUBLIC_PREFIX = 'NEXT_PUBLIC_';
const VALID_KEY = /^[A-Z][A-Z0-9_]*$/;

export function classifyEnvKey(name: string): EnvClassification {
  const key = name.trim();
  if (INVALID_KEY.test(key) || !VALID_KEY.test(key)) return 'invalid';
  if (key.startsWith(PUBLIC_PREFIX)) return 'public';
  return 'server';
}

Build the two views

The client view redacts server values. The server view returns every non-invalid entry.

export function redactForClient(entries: EnvEntry[]): Record<string, string> {
  const out: Record<string, string> = {};
  for (const entry of entries) {
    const kind = classifyEnvKey(entry.key);
    if (kind === 'public') {
      out[entry.key] = entry.value;
    } else if (kind === 'server') {
      out[entry.key] = '[REDACTED - server only]';
    }
  }
  return out;
}

export function serverView(entries: EnvEntry[]): Record<string, string> {
  const out: Record<string, string> = {};
  for (const entry of entries) {
    if (classifyEnvKey(entry.key) !== 'invalid') {
      out[entry.key] = entry.value;
    }
  }
  return out;
}

Render both as JSON

The demo memoizes each step with useMemo and prints JSON.stringify(view, null, 2) for each panel.

Use Cases

  • Teaching why putting a secret behind a NEXT_PUBLIC_ prefix is a leak.
  • Reviewing a .env.example before committing it, to see which entries would end up in browser code.
  • Demonstrating key naming conventions (uppercase letters, digits, and underscores).
  • Onboarding material for configuring Preview versus Production on Vercel.

Limitations

Security note from the registry:

  • Demo uses dummy values only - never loads real process secrets into the client.

Limits and behaviors taken from the code:

  • Invalid keys disappear silently. A line such as lower_case=1 parses successfully but classifies as invalid, and it is then omitted from both views without an error message. Only lines without a usable KEY=value shape produce an error.
  • Not a full dotenv parser. There is no support for export KEY=value (the key would be export KEY and be invalid), inline comments after a value, multi-line values, or variable expansion.
  • Duplicate keys. Later lines overwrite earlier ones in the output objects, and no warning is shown.
  • Simulation of the client bundle. The client view is a redaction model. It does not run a Next.js build, so it does not prove that a value was inlined or that your code referenced it. In real Next.js, NEXT_PUBLIC_ values are inlined at build time wherever your code reads them.
  • Server view shows dummy values in clear. This is intentional because the data is fake; never paste real credentials.
  • Zod is listed, not used. The registry lists Zod as a technology, but this experiment's code does not import it. The classification is a regular expression and string prefix check.

Use in your project

Reuse the classification and redaction helpers to lint a .env.example, or add runtime validation with Zod on the server:

// lib/env-classify.ts
const PUBLIC_PREFIX = 'NEXT_PUBLIC_';
const VALID_KEY = /^[A-Z][A-Z0-9_]*$/;

export type EnvClassification = 'public' | 'server' | 'invalid';

export function classifyEnvKey(name: string): EnvClassification {
  const key = name.trim();
  if (!VALID_KEY.test(key)) return 'invalid';
  return key.startsWith(PUBLIC_PREFIX) ? 'public' : 'server';
}
// lib/env.ts - optional server-side validation (an extension, not part of the experiment)
import { z } from 'zod';

const serverEnv = z.object({
  DATABASE_URL: z.string().min(1),
  API_SECRET_KEY: z.string().min(1),
});

const publicEnv = z.object({
  NEXT_PUBLIC_SITE_URL: z.url(),
});

export const env = {
  ...serverEnv.parse(process.env),
  ...publicEnv.parse(process.env),
};

Keep a file that imports secrets out of any Client Component import graph, for example by marking it with the server-only package.

Deployment

Deploy on Vercel

No Marketplace stores or secrets are needed, and no experiment-specific deploy button is registered. A plain Vercel deploy works via the Deploy button on this experiment page. When you apply what you learn here, configure variables in the Vercel project settings per environment.

Local Development

pnpm install
pnpm dev

Open the experiment page and try these edits:

  • Add MY_FLAG=on and see it redacted in the client view.
  • Rename it to NEXT_PUBLIC_MY_FLAG=on and see the value appear.
  • Add bad line and see Line N: expected KEY=value.
  • Add lowercase=1 and notice that it vanishes from both views.

Unit tests are in apps/experiments/environment-variable-inspector/logic.test.ts:

pnpm test

This experiment has no API routes, so there are no curl examples.

Configuration

ItemRequiredNotes
Environment variablesNoThe experiment never reads process.env.
External servicesNoNone.

Vercel / Next.js Features Used

Next Steps

On this page