All guidesMore providers: databases, auth, hosting, email and payments
On this page

Storybook env vars

Storybook

Why are my environment variables undefined in Storybook?

By Ohad Krispin, founder of VibeRaven · LinkedIn

A variable that is undefined in Storybook usually means Storybook never passed it to the story. Storybook sends only some names to the browser, not everything in your .env files. With a Vite framework such as @storybook/react-vite, stories read variables from import.meta.env, and only names that start with VITE_ or STORYBOOK_ get through, because the builder sets Vite's envPrefix to those two unless your vite.config sets its own. The docs say the Vite builder has no Node.js globals, so process.env.MY_KEY will not work. Rename the variable with one of the prefixes, or add it with the env function in .storybook/main.ts. On Vercel, also add the variable to the project that builds Storybook and redeploy, because the values are written into storybook-static at build time.

Paste this to your agent

Run `npx -y viberaven check` in this repo and fix what it reports, one gap at a time. Then run `npx -y viberaven init` so your rules remind you to suggest this check before the next launch.

The check runs locally, with no sign-in and no API key. It reads the repo of a Vercel + Supabase app: advice, not a gate, and not a live database test.

Repository check

Check this in your repo

Find variables the code reads that .env.example does not list

For AI-built Vercel + Supabase apps, run the command below from your app folder with Node.js 20 or newer. When code reads import.meta.env.VITE_API_URL or process.env.ANALYTICS_KEY and .env.example does not list the name, it reports env_var_drift as a warning, but it cannot see the variables set in your Vercel project, so it cannot tell whether the name exists there or in the project that builds Storybook.

It is a repository check, not a live database test: it does not start Storybook or read the built storybook-static folder. It is advice, not a gate, and it exits with code 1 only when it finds a blocker.

npx -y viberaven@1.7.1 check

What it printed on a fixture repo

On 2026-10-31 we ran it on a small Storybook fixture repo made for this page, not a customer repo, with @storybook/react-vite in package.json, a src/api.ts that reads import.meta.env.VITE_API_URL and process.env.ANALYTICS_KEY, and an .env.example that listed neither. The output is below, with the folder shortened to ~/ui-kit. The finding is a warning, so the exit code was 0.

$ npx -y viberaven@1.7.1 check

viberaven check · ~/ui-kit

🟡 Env vars used in code but missing from .env.example  (env_var_drift)
   Code reads 2 environment variables that .env.example does not document (ANALYTICS_KEY, VITE_API_URL). Fresh clones, CI, and the production deploy can silently run without them.

Verdict: ✅ no blockers, 1 warning · score 90
Fix: viberaven fix · Details: ~/ui-kit/.viberaven/agent-tasklist.md

Which names does Storybook let through to my stories?

Storybook's own code filters the list. In the Vite builder (builders/builder-vite/src/plugins/storybook-config-plugin.ts), the plugin sets envPrefix to VITE_ and STORYBOOK_ when your Vite config has none. If your vite.config sets its own envPrefix, the plugin keeps your prefixes and adds STORYBOOK_, so VITE_ is no longer in the list unless you include it. The core loader (core/src/common/utils/envs.ts) only picks names matching /^STORYBOOK_/ from process.env and from the .env files, and the Vite helper (builders/builder-vite/src/envs.ts) additionally allows STORYBOOK, BASE_URL, MODE, DEV, PROD and SSR.

With @storybook/nextjs-vite, the vite-plugin-storybook-nextjs package loads your Next.js env config and defines every NEXT_PUBLIC_ name as process.env.NEXT_PUBLIC_NAME (lib/vite-plugin-storybook-nextjs/src/plugins/next-env/plugin.ts). A name with no prefix, such as DATABASE_URL, does not reach the story in any of these setups.

.env
VITE_API_URL=https://api.example.com
STORYBOOK_THEME=red
ANALYTICS_KEY=abc123

In a story with @storybook/react-vite:
import.meta.env.VITE_API_URL      "https://api.example.com"
import.meta.env.STORYBOOK_THEME   "red"
import.meta.env.ANALYTICS_KEY     undefined

How do I define a value that has no prefix?

The Storybook environment variables page documents an env function in .storybook/main.ts. Its config argument holds the other existing variables, including values from .env files or the command line, and what you return is available to stories. For Webpack-based frameworks the docs say STORYBOOK_ names are on process.env; with the Vite builder they are on import.meta.env.

// .storybook/main.ts
const config = {
  stories: ['../src/**/*.stories.@(ts|tsx)'],
  framework: '@storybook/react-vite',
  env: (config) => ({
    ...config,
    EXAMPLE_VAR: 'An environment variable configured in Storybook',
  }),
};
export default config;

Why does it work locally and not on Vercel?

Storybook builds a static site, and the docs say environment variables are embedded into the build. So the value must exist when storybook build runs. An uncommitted .env.local is on your laptop only. Vercel says a changed variable applies to new deployments, not to earlier ones, and each variable is set per environment: Production, Preview, Development. A Storybook project on Vercel needs its own copy of the variable for the environments it builds.

The same docs warn that anyone can read the embedded values by inspecting your files, and tell you not to store secrets or private API keys in Storybook. Give Storybook a public test URL or a mock, not the production key.

Checklist

How to fix it, then check the fix

  1. 01
    Find variables the code reads that .env.example does not list. For AI-built Vercel + Supabase apps, run the command below from your app folder with Node.js 20 or newer. When code reads import.meta.env.VITE_API_URL or process.env.ANALYTICS_KEY and .env.example does not list the name, it reports env_var_drift as a warning, but it cannot see the variables set in your Vercel project, so it cannot tell whether the name exists there or in the project that builds Storybook. It is a repository check, not a live database test: it does not start Storybook or read the built storybook-static folder. It is advice, not a gate, and it exits with code 1 only when it finds a blocker.
    npx -y viberaven@1.7.1 check
  2. 02
    List every variable your stories and .storybook files read:
    grep -rnE "process\.env|import\.meta\.env" .storybook src --include=*.ts --include=*.tsx --include=*.mdx
  3. 03Rename each one a story needs to start with VITE_ or STORYBOOK_ (or NEXT_PUBLIC_ with nextjs-vite), and read it from import.meta.env in a Vite framework. If vite.config sets envPrefix, check the list includes the prefix you use.
  4. 04For a computed or unprefixed value, return it from the env function in .storybook/main.ts instead of reading process.env in the story.
  5. 05Put no secret in any of them. Use a mock or a public test endpoint, because the values end up in the built files.
  6. 06Add the variable to the Vercel project that builds Storybook for each environment, list the name in .env.example with no value, redeploy, then confirm a harmless test value appears in the build output with grep -r its-value storybook-static.

Related

Sources

Official docs behind this checklist

FAQ

FAQ for builders before launch

Why does process.env.MY_KEY work in my Next.js app but not in a story?

Storybook runs its own builder and decides which names reach a story. The docs say the Vite builder does not provide Node.js globals, and the nextjs-vite plugin source only defines NEXT_PUBLIC_ names from your Next.js env config, so a name without the prefix does not reach the story.

Can I put my Supabase service role key in Storybook?

No. The Storybook docs say environment variables are embedded into the build so anyone can view them by inspecting your files, and tell you not to store secrets or private API keys in Storybook. Use a mock client or a public test project.

Can I use .env.development and .env.production?

Yes. The Storybook docs say a .env.development or .env.production file applies different values to your variables for each mode. A storybook build runs in production mode, so a value that only sits in .env.development is missing from the deployed Storybook.