monorepo·vite·nextjs·2 min read
Sharing Env Vars in a Monorepo with Vite and Next.js
Keep a single .env.shared at the root of your monorepo and generate per-app .env files with the right Vite and Next.js prefixes.

In a monorepo with Vite and Next.js apps there's an annoying detail: each framework requires a different prefix to expose vars to the client. Vite uses VITE_PUBLIC_, Next.js uses NEXT_PUBLIC_. How do you keep a single source of truth for shared variables?
A simple solution: a .env.shared at the root, and a script that generates per-app .env files with the correct prefixes.
Step 1: create .env.shared
At the root of the monorepo:
SECRET_KEY=your_secret_key
API_URL=your_api_url
Step 2: generation script
Create a generate-env.js at the root:
const fs = require('fs');
const path = require('path');
const envShared = path.join(__dirname, '.env.shared');
const envVite = path.join(__dirname, 'vite-project', '.env');
const envNext = path.join(__dirname, 'next-project', '.env');
const sharedVars = fs.readFileSync(envShared, 'utf-8').split('\n');
const viteVars = sharedVars.map((line) => {
if (line.startsWith('SECRET_KEY') || line.startsWith('API_URL')) {
return `VITE_PUBLIC_${line}`;
}
return line;
});
const nextVars = sharedVars.map((line) => {
if (line.startsWith('SECRET_KEY') || line.startsWith('API_URL')) {
return `NEXT_PUBLIC_${line}`;
}
return line;
});
fs.writeFileSync(envVite, viteVars.join('\n'), 'utf-8');
fs.writeFileSync(envNext, nextVars.join('\n'), 'utf-8');
Step 3: run the script
node generate-env.js
This creates a .env in vite-project/ prefixed with VITE_PUBLIC_ and a .env in next-project/ prefixed with NEXT_PUBLIC_.
Best practices
- Add generated
.envfiles to.gitignoreto avoid leaking secrets. - Run the script whenever
.env.sharedchanges. - Add the script as a step in your build or deploy pipeline.
- For something more robust, consider dotenv-vault or an internal monorepo package exposing a typed config.
May 4, 2023 · Brazil