name: t3-env-validation description: Type-safe environment variable validation with @t3-oss/env-nextjs and Zod. Build-time validation ensures all required env vars are present and correctly typed. allowed-tools: Read, Write, Edit, Glob, Grep, Bash, mcp__context7__resolve-library-id, mcp__context7__get-library-docs
T3 Env Validation Skill
Type-safe environment variable validation with @t3-oss/env-nextjs and Zod.
Reference Files:
- setup.md - Configuration patterns
- schema-patterns.md - Common Zod validation patterns
- testing.md - Mocking env vars in tests
- examples.md - Practical examples
Project Configuration
Environment variables are validated in two files:
| File | Purpose | Variables |
|---|---|---|
data/env/server.ts |
Server-only vars | CLERK_SECRET_KEY, Firebase, etc. |
data/env/client.ts |
Public vars | NEXT_PUBLIC_* variables |
Quick Start
Adding Server Environment Variable
// data/env/server.ts
import { z } from "zod";
import { createEnv } from "@t3-oss/env-nextjs";
export const env = createEnv({
server: {
// Add new variable
DATABASE_URL: z.string().url(),
API_SECRET: z.string().min(32),
},
experimental__runtimeEnv: process.env,
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
emptyStringAsUndefined: true,
});
Adding Client Environment Variable
// data/env/client.ts
import { z } from "zod";
import { createEnv } from "@t3-oss/env-nextjs";
export const env = createEnv({
client: {
// Must be prefixed with NEXT_PUBLIC_
NEXT_PUBLIC_API_URL: z.string().url(),
},
// Must manually map client vars
experimental__runtimeEnv: {
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
},
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
emptyStringAsUndefined: true,
});
Usage in Code
// Server-side (Server Components, Server Actions, API routes)
import { env } from "~/data/env/server";
const apiSecret = env.API_SECRET; // Type-safe!
// Client-side (Client Components)
import { env } from "~/data/env/client";
const apiUrl = env.NEXT_PUBLIC_API_URL; // Type-safe!
Key Concepts
Build-Time Validation
T3 Env validates environment variables at build time:
npm run build
❌ Invalid environment variables:
CLERK_SECRET_KEY: Required
FIREBASE_PROJECT_ID: Required
This prevents deploying with missing configuration.
Server vs Client Variables
| Type | Prefix | Accessible In | Bundled |
|---|---|---|---|
| Server | None | Server Components, Actions, API | No |
| Client | NEXT_PUBLIC_ |
Everywhere | Yes (in JS bundle) |
Security Rule: Never put secrets in client variables!
Empty String Handling
emptyStringAsUndefined: true;
With this setting:
VAR=""is treated as undefined- Required vars with empty string will fail validation
- Optional vars with empty string use default
Common Patterns
Required String
API_KEY: z.string();
Optional with Default
LOG_LEVEL: z.enum(["debug", "info", "warn", "error"])
.optional()
.default("info");
Boolean Transform
FEATURE_FLAG: z.enum(["true", "false"])
.optional()
.transform((val) => val === "true");
URL Validation
API_URL: z.string().url();
DATABASE_URL: z.string().url().startsWith("postgresql://");
Number Transform
PORT: z.string().transform((val) => parseInt(val, 10));
TIMEOUT_MS: z.coerce.number().int().positive();
File Locations
| Purpose | Location |
|---|---|
| Server env | data/env/server.ts |
| Client env | data/env/client.ts |
| Env template | .env.example |
| Local env | .env.local |
Troubleshooting
Build Fails with Missing Env
# Skip validation (for Docker builds)
SKIP_ENV_VALIDATION=true npm run build
Variable Not Available
- Server var → Can only be used in server code
- Client var → Must be in
experimental__runtimeEnv - Check spelling and
NEXT_PUBLIC_prefix
Type Errors
// Import from correct file
import { env } from "~/data/env/server"; // For server vars
import { env } from "~/data/env/client"; // For client vars
Related Skills
server-actions- Using env vars in server actionsfirebase-firestore- Firebase env configurationclerk-auth-proxy- Clerk env configuration