| name | solidstart-environment |
| description | SolidStart environment variables: VITE_ prefix for public variables, process.env for server-only, .env files, type safety with env.d.ts, runtime vs build-time variables. |
| metadata | {"globs":["**/.env*","**/env.d.ts","**/*env*"]} |
SolidStart Environment Variables
Complete guide to managing environment variables in SolidStart. Understand the difference between public (client-side) and private (server-only) variables.
Public Environment Variables
Public variables are safe to expose to client-side code. They must be prefixed with VITE_ and are injected during build time.
Basic Setup
Create a .env file in the project root:
VITE_API_URL=https://api.example.com
VITE_APP_NAME=My App
VITE_USER_ID=123
Access in client code:
function MyComponent() {
return (
<div>
<h2>API: {import.meta.env.VITE_API_URL}</h2>
<p>App: {import.meta.env.VITE_APP_NAME}</p>
</div>
);
}
Key points:
- Must use
VITE_ prefix
- Injected at build time
- Available in client code
- Access via
import.meta.env.VITE_*
Type Safety
Create env.d.ts for TypeScript autocomplete:
interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_APP_NAME: string;
readonly VITE_USER_ID: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
Now TypeScript will autocomplete and type-check your environment variables.
Private Environment Variables
Private variables are server-only and should NOT use the VITE_ prefix. Access them via process.env in server code.
Basic Setup
DB_HOST=somedb://192.110.0
DB_PASSWORD=super_secret_password_hash
API_SECRET_KEY=secret123
Access in server code:
"use server";
export async function getData() {
const client = new DB({
host: process.env.DB_HOST,
password: process.env.DB_PASSWORD,
});
const apiKey = process.env.API_SECRET_KEY;
return client.query();
}
Key points:
- NO
VITE_ prefix
- Only accessible in server code
- Use
process.env to access
- Not exposed to client
Type Safety for Server Variables
Add to env.d.ts:
declare namespace NodeJS {
interface ProcessEnv {
readonly DB_HOST: string;
readonly DB_PASSWORD: string;
readonly API_SECRET_KEY: string;
}
}
Environment Files
Development (.env)
VITE_API_URL=http://localhost:3000
DB_HOST=localhost
DB_PASSWORD=dev_password
Production (.env.production)
VITE_API_URL=https://api.production.com
DB_HOST=prod-db.example.com
DB_PASSWORD=prod_secret_password
Local Override (.env.local)
# Overrides .env, not committed to git
VITE_API_URL=http://localhost:4000
File priority (highest to lowest):
.env.local (always loaded, ignored by git)
.env.[mode].local (e.g., .env.production.local)
.env.[mode] (e.g., .env.production)
.env
Security Best Practices
❌ Never Expose Secrets
# ❌ WRONG - This will be exposed to client!
VITE_DB_PASSWORD=secret123
# ✅ CORRECT - No VITE_ prefix
DB_PASSWORD=secret123
✅ Verify Client Exposure
console.log(import.meta.env.VITE_SECRET_KEY);
console.log(import.meta.env.DB_PASSWORD);
✅ Use Server Actions for Secrets
function ClientComponent() {
const apiKey = import.meta.env.VITE_API_KEY;
fetch(`/api?key=${apiKey}`);
}
"use server";
export async function fetchData() {
const apiKey = process.env.API_SECRET_KEY;
return fetch(`/api?key=${apiKey}`);
}
Runtime vs Build-Time
Build-Time Variables (VITE_*)
const apiUrl = import.meta.env.VITE_API_URL;
Characteristics:
- Replaced during build
- Different values per build
- Cannot change at runtime
- Bundled into client code
Runtime Variables (Server)
const dbHost = process.env.DB_HOST;
Characteristics:
- Accessed at runtime
- Can change without rebuild
- Server-only access
- Not bundled
Common Patterns
API Configuration
# .env
VITE_API_URL=http://localhost:3000
VITE_API_TIMEOUT=5000
const apiUrl = import.meta.env.VITE_API_URL;
const timeout = Number(import.meta.env.VITE_API_TIMEOUT) || 5000;
fetch(`${apiUrl}/data`, { signal: AbortSignal.timeout(timeout) });
Database Configuration
# .env (server-only)
DATABASE_URL=postgresql://user:pass@localhost:5432/db
REDIS_URL=redis://localhost:6379
"use server";
export async function connectDB() {
const db = new Database(process.env.DATABASE_URL);
const redis = new Redis(process.env.REDIS_URL);
return { db, redis };
}
Feature Flags
# .env
VITE_ENABLE_ANALYTICS=true
VITE_ENABLE_DEBUG=false
const enableAnalytics = import.meta.env.VITE_ENABLE_ANALYTICS === "true";
const enableDebug = import.meta.env.VITE_ENABLE_DEBUG === "true";
if (enableAnalytics) {
initAnalytics();
}
if (enableDebug) {
console.log("Debug mode enabled");
}
Environment-Specific Config
export const config = {
apiUrl: import.meta.env.VITE_API_URL || "http://localhost:3000",
isDev: import.meta.env.DEV,
isProd: import.meta.env.PROD,
mode: import.meta.env.MODE,
};
TypeScript Setup
Complete env.d.ts example:
interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_APP_NAME: string;
readonly VITE_ENABLE_ANALYTICS: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
declare namespace NodeJS {
interface ProcessEnv {
readonly DATABASE_URL: string;
readonly DB_PASSWORD: string;
readonly API_SECRET_KEY: string;
readonly REDIS_URL: string;
}
}
Vite Built-in Variables
Vite provides built-in environment variables:
import.meta.env.MODE
import.meta.env.DEV
import.meta.env.PROD
import.meta.env.SSR
import.meta.env.BASE_URL
Deployment Considerations
Platform-Specific Setup
Different platforms handle environment variables differently:
Vercel:
- Set in dashboard or
vercel.json
- Automatically available as
process.env
Netlify:
- Set in dashboard or
netlify.toml
- Available as
process.env
Node.js:
- Use
.env files with dotenv
- Or set in system environment
Build-Time vs Runtime
const apiUrl = import.meta.env.VITE_API_URL;
const dbUrl = process.env.DATABASE_URL;
Best Practices
-
Always prefix public variables with VITE_:
- Prevents accidental exposure
- Clear distinction between public/private
-
Never use VITE_ for secrets:
- Secrets should be server-only
- Use
process.env without prefix
-
Use TypeScript for type safety:
- Define in
env.d.ts
- Get autocomplete and type checking
-
Use .env.local for local overrides:
- Not committed to git
- Overrides other env files
-
Document required variables:
- List in README
- Provide
.env.example template
-
Validate environment variables:
const requiredEnv = {
apiUrl: import.meta.env.VITE_API_URL,
};
if (!requiredEnv.apiUrl) {
throw new Error("VITE_API_URL is required");
}
Summary
- Public variables:
VITE_ prefix, import.meta.env, client-accessible
- Private variables: No prefix,
process.env, server-only
- Type safety: Define in
env.d.ts
- Security: Never expose secrets with
VITE_ prefix
- Build-time: VITE_ variables replaced during build
- Runtime: Server variables accessed at runtime