Typesafe environment variable validation and management for Next.js applications with runtime compatibility enforcement.
—
Core functionality for creating typesafe environment variable schemas with Next.js-specific validation rules and runtime compatibility enforcement.
Creates a new environment variable schema with Next.js-specific configurations, enforcing the NEXT_PUBLIC_ prefix for client-side variables and providing runtime validation.
/**
* Create a new environment variable schema with Next.js-specific configurations
* @param opts - Configuration options for the environment schema
* @returns Validated environment variables with full type safety
*/
function createEnv<
TServer extends StandardSchemaDictionary = NonNullable<unknown>,
TClient extends Record<`NEXT_PUBLIC_${string}`, StandardSchemaV1> = NonNullable<unknown>,
TShared extends StandardSchemaDictionary = NonNullable<unknown>,
const TExtends extends Array<Record<string, unknown>> = [],
TFinalSchema extends StandardSchemaV1<{}, {}> = DefaultCombinedSchema<TServer, TClient, TShared>
>(opts: Options<TServer, TClient, TShared, TExtends, TFinalSchema>): CreateEnv<TFinalSchema, TExtends>;Generic Parameters:
TServer - Schema dictionary for server-side environment variablesTClient - Schema dictionary for client-side environment variables (must use NEXT_PUBLIC_ prefix)TShared - Schema dictionary for shared environment variables (available on both server and client)TExtends - Array of preset environment objects to extend fromTFinalSchema - Final combined schema typeConfiguration options for the createEnv function with Next.js-specific runtime environment handling.
type Options<
TServer extends StandardSchemaDictionary,
TClient extends Record<`NEXT_PUBLIC_${string}`, StandardSchemaV1>,
TShared extends StandardSchemaDictionary,
TExtends extends Array<Record<string, unknown>>,
TFinalSchema extends StandardSchemaV1<{}, {}>
> = Omit<
StrictOptions<"NEXT_PUBLIC_", TServer, TClient, TShared, TExtends> &
ServerClientOptions<"NEXT_PUBLIC_", TServer, TClient> &
CreateSchemaOptions<TServer, TClient, TShared, TFinalSchema>,
"runtimeEnvStrict" | "runtimeEnv" | "clientPrefix"
> & (
| {
/** Manual destruction of process.env. Required for Next.js < 13.4.4 */
runtimeEnv: StrictOptions<"NEXT_PUBLIC_", TServer, TClient, TShared, TExtends>["runtimeEnvStrict"];
experimental__runtimeEnv?: never;
}
| {
runtimeEnv?: never;
/**
* Can be used for Next.js ^13.4.4 since they stopped static analysis of server side process.env.
* Only client side process.env is statically analyzed and needs to be manually destructured.
*/
experimental__runtimeEnv: Record<
| {
[TKey in keyof TClient]: TKey extends `NEXT_PUBLIC_${string}` ? TKey : never;
}[keyof TClient]
| {
[TKey in keyof TShared]: TKey extends string ? TKey : never;
}[keyof TShared],
string | boolean | number | undefined
>;
}
);Configuration Properties:
Schema Definition:
server?: TServer - Server-side environment variable schemas (not available on client)client?: TClient - Client-side environment variable schemas (must be prefixed with NEXT_PUBLIC_)shared?: TShared - Shared environment variable schemas (available on both server and client)extends?: TExtends - Array of preset environment objects to extend fromRuntime Environment:
runtimeEnv?: Record<string, any> - Manual process.env destruction for Next.js < 13.4.4experimental__runtimeEnv?: Record<string, any> - Experimental runtime environment for Next.js >= 13.4.4Base Configuration Options:
isServer?: boolean - How to determine whether the app is running on the server or the client (default: typeof window === "undefined")onValidationError?: (issues: readonly StandardSchemaV1.Issue[]) => never - Called when validation fails (default: logs error and throws)onInvalidAccess?: (variable: string) => never - Called when server-side environment variable is accessed on client (default: throws error)skipValidation?: boolean - Whether to skip validation of environment variables (default: false)emptyStringAsUndefined?: boolean - Treat empty strings as undefined for better default value handling (default: false)createFinalSchema?: (shape: TServer & TClient & TShared, isServer: boolean) => TFinalSchema - Custom function to combine the schemasimport { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
const env = createEnv({
server: {
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
},
client: {
NEXT_PUBLIC_APP_URL: z.string().url(),
NEXT_PUBLIC_ANALYTICS_ID: z.string().min(1),
},
runtimeEnv: {
DATABASE_URL: process.env.DATABASE_URL,
JWT_SECRET: process.env.JWT_SECRET,
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
NEXT_PUBLIC_ANALYTICS_ID: process.env.NEXT_PUBLIC_ANALYTICS_ID,
},
});import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
const env = createEnv({
server: {
DATABASE_URL: z.string().url(),
},
client: {
NEXT_PUBLIC_APP_URL: z.string().url(),
},
shared: {
NODE_ENV: z.enum(["development", "test", "production"]),
APP_VERSION: z.string().default("1.0.0"),
},
runtimeEnv: {
DATABASE_URL: process.env.DATABASE_URL,
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
NODE_ENV: process.env.NODE_ENV,
APP_VERSION: process.env.APP_VERSION,
},
});import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
const env = createEnv({
server: {
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
},
client: {
NEXT_PUBLIC_APP_URL: z.string().url(),
},
shared: {
NODE_ENV: z.enum(["development", "test", "production"]),
},
// Only specify client and shared variables
experimental__runtimeEnv: {
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
NODE_ENV: process.env.NODE_ENV,
},
});import { createEnv } from "@t3-oss/env-nextjs";
import { vercel } from "@t3-oss/env-nextjs/presets-zod";
import { z } from "zod";
const preset = vercel();
const env = createEnv({
server: {
DATABASE_URL: z.string().url(),
},
client: {
NEXT_PUBLIC_APP_URL: z.string().url(),
},
extends: [preset],
runtimeEnv: {
DATABASE_URL: process.env.DATABASE_URL,
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
// Preset variables are automatically available
},
});import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
const env = createEnv({
server: {
PORT: z.string().transform(Number),
ENABLE_LOGGING: z.string().transform((val) => val === "true"),
},
client: {
NEXT_PUBLIC_API_URL: z.string().url(),
},
runtimeEnv: {
PORT: process.env.PORT,
ENABLE_LOGGING: process.env.ENABLE_LOGGING,
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
},
});
// env.PORT is now a number
// env.ENABLE_LOGGING is now a booleanThe package throws validation errors in the following scenarios:
// This will cause a compile-time error
createEnv({
server: {
// @ts-expect-error - server variables cannot have NEXT_PUBLIC_ prefix
NEXT_PUBLIC_SECRET: z.string(),
},
client: {
// @ts-expect-error - client variables must have NEXT_PUBLIC_ prefix
API_KEY: z.string(),
},
runtimeEnv: {},
});Install with Tessl CLI
npx tessl i tessl/npm-t3-oss--env-nextjs