From harness-claude
Configures and validates Next.js environment variables securely with Zod and t3-env, enforces server-only boundaries, exposes safe client vars, and handles multi-environment setups.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Manage environment variables, runtime config, and server-only module boundaries safely
Manages Node.js environment configuration using process.env, dotenv, and Zod validation for 12-factor apps. Validates required variables at startup and enables type-safe access across dev, staging, and production.
Manages env vars with better-env: typed config schemas, Vercel sync from local files, prebuild validation. Useful for type-safe envs in Vercel deploys.
Guides deploying Next.js apps to Vercel covering environment variables setup across environments, edge vs serverless functions, build optimization, and preview deployments.
Share bugs, ideas, or general feedback.
Manage environment variables, runtime config, and server-only module boundaries safely
NEXT_PUBLIC_ prefixNEXT_PUBLIC_ to expose them to the browser — all other variables are server-only.NEXT_PUBLIC_ variables — they are inlined into the client bundle.t3-env or @t3-oss/env-nextjs to validate environment variables at build time with Zod schemas.src/env.ts (or lib/env.ts) as the single source of truth for env var access — import env values from here, never from process.env directly in components.import 'server-only' to any module that accesses server-only env vars to prevent accidental client import..env.local for local development secrets — it is gitignored by default. Use .env.development and .env.production for non-secret environment-specific values.next.config.ts with your env validation so the build fails immediately on missing required variables.next.config.ts via process.env — variables set in hosting dashboards (Vercel, Railway) are available at runtime.// src/env.ts — validated env with t3-env
import { createEnv } from '@t3-oss/env-nextjs';
import { z } from 'zod';
export const env = createEnv({
server: {
DATABASE_URL: z.string().url(),
NEXTAUTH_SECRET: z.string().min(32),
STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
},
client: {
NEXT_PUBLIC_APP_URL: z.string().url(),
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string().startsWith('pk_'),
},
runtimeEnv: {
DATABASE_URL: process.env.DATABASE_URL,
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
},
});
// lib/db.ts — import 'server-only' prevents client bundle inclusion
import 'server-only';
import { env } from '@/env';
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
const client = postgres(env.DATABASE_URL);
export const db = drizzle(client);
Next.js treats environment variables differently depending on when and where they are accessed:
Build-time inlining: NEXT_PUBLIC_ variables are replaced with their literal values at build time (process.env.NEXT_PUBLIC_API_URL becomes "https://api.example.com" in the bundle). This means the value is fixed at build time and cannot change at runtime without rebuilding.
Server-only variables: Variables without NEXT_PUBLIC_ are only available in Server Components, Route Handlers, and other server-side code. Accessing them in a Client Component returns undefined and does not leak the value — it simply does not work.
.env file priority: Next.js loads .env, .env.local, .env.development/.env.production, and .env.development.local/.env.production.local. More specific files override less specific ones. .local files take highest priority and should be in .gitignore.
Runtime vs build-time: Variables set in the Vercel dashboard are available at runtime in server-side code. However, NEXT_PUBLIC_ variables must be set at build time in Vercel — setting them in the dashboard alone requires a rebuild.
t3-env benefits: Without validation, a missing DATABASE_URL crashes the app at the first database query — potentially after deployment. t3-env fails the build or server startup immediately with a clear error listing every missing or invalid variable.
server-only package: import 'server-only' causes a build error if the module is imported in a Client Component. Use it in any module that accesses secrets, database clients, or other server-only resources. Pair with client-only for the inverse — marking browser-only modules.
https://nextjs.org/docs/app/building-your-application/configuring/environment-variables