or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core.mdindex.mdpresets-arktype.mdpresets-valibot.mdpresets-zod.md
tile.json

tessl/npm-t3-oss--env-nextjs

Typesafe environment variable validation and management for Next.js applications with runtime compatibility enforcement.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@t3-oss/env-nextjs@0.13.x

To install, run

npx @tessl/cli install tessl/npm-t3-oss--env-nextjs@0.13.0

index.mddocs/

@t3-oss/env-nextjs

@t3-oss/env-nextjs provides typesafe environment variable validation and management specifically designed for Next.js applications. This package automatically enforces Next.js naming conventions (NEXT_PUBLIC_ prefix for client variables), ensures compatibility across all Next.js runtime environments (Node.js server, Edge Runtime, and browser client), and provides compile-time type checking with runtime validation using Standard Schema-compliant validators.

Package Information

  • Package Name: @t3-oss/env-nextjs
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @t3-oss/env-nextjs

Core Imports

import { createEnv } from "@t3-oss/env-nextjs";

For CommonJS:

const { createEnv } = require("@t3-oss/env-nextjs");

Basic Usage

import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";

export const env = createEnv({
  // Server-side environment variables (not available on client)
  server: {
    DATABASE_URL: z.string().url(),
    OPEN_AI_API_KEY: z.string().min(1),
  },
  // Client-side environment variables (requires NEXT_PUBLIC_ prefix)
  client: {
    NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
  },
  // Shared environment variables (available on both server and client)
  shared: {
    NODE_ENV: z.enum(["development", "test", "production"]),
  },
  // Runtime environment values
  runtimeEnv: {
    DATABASE_URL: process.env.DATABASE_URL,
    OPEN_AI_API_KEY: process.env.OPEN_AI_API_KEY,
    NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
    NODE_ENV: process.env.NODE_ENV,
  },
});

// Access environment variables with full type safety
console.log(env.DATABASE_URL); // string
console.log(env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY); // string
console.log(env.NODE_ENV); // "development" | "test" | "production"

Architecture

@t3-oss/env-nextjs is built around several key components:

  • Core Function: createEnv function that creates typesafe environment variable schemas with Next.js-specific configurations
  • Client Prefix Enforcement: Automatic validation that client-side variables use the required NEXT_PUBLIC_ prefix
  • Runtime Environment Management: Support for both traditional runtimeEnv and experimental experimental__runtimeEnv modes
  • Type Safety: Full TypeScript integration with compile-time type checking and runtime validation
  • Standard Schema Compatibility: Works with any Standard Schema-compliant validator (Zod, Valibot, Arktype)
  • Preset System: Pre-configured environment variable schemas for popular hosting platforms and services

Capabilities

Environment Schema Creation

Core functionality for creating typesafe environment variable schemas with Next.js-specific validation rules and runtime compatibility enforcement.

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>;

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"
> & (
  | {
      runtimeEnv: StrictOptions<"NEXT_PUBLIC_", TServer, TClient, TShared, TExtends>["runtimeEnvStrict"];
      experimental__runtimeEnv?: never;
    }
  | {
      runtimeEnv?: never;
      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
      >;
    }
);

Environment Schema Creation

Zod Presets

Pre-configured environment variable schemas for popular hosting platforms and services using Zod validators. Includes presets for Vercel, Railway, Render, Netlify, and many other platforms.

// Example preset functions
function vercel(): Readonly<VercelEnv>;
function railway(): Readonly<RailwayEnv>;
function render(): Readonly<RenderEnv>;
function netlify(): Readonly<NetlifyEnv>;

Zod Presets

Arktype Presets

Pre-configured environment variable schemas using Arktype validators. Provides the same platform presets as Zod but using Arktype's type system for validation.

// Example preset functions  
function vercel(): Readonly<VercelEnv>;
function railway(): Readonly<RailwayEnv>;
function render(): Readonly<RenderEnv>;
function netlify(): Readonly<NetlifyEnv>;

Arktype Presets

Valibot Presets

Pre-configured environment variable schemas using Valibot validators. Provides the same platform presets as Zod and Arktype but using Valibot's validation system.

// Example preset functions
function vercel(): Readonly<VercelEnv>;
function railway(): Readonly<RailwayEnv>;
function render(): Readonly<RenderEnv>;
function netlify(): Readonly<NetlifyEnv>;

Valibot Presets

Types

// Client prefix type
type ClientPrefix = "NEXT_PUBLIC_";

// Standard schema types (from @t3-oss/env-core)
interface StandardSchemaDictionary {
  [key: string]: StandardSchemaV1;
}

interface StandardSchemaV1<Input = any, Output = Input> {
  readonly "~standard": {
    readonly version: 1;
    readonly vendor: string;
    readonly validate: (value: unknown) => StandardSchemaV1.Result<Output> | Promise<StandardSchemaV1.Result<Output>>;
    readonly types?: StandardSchemaV1.Types<Input, Output> | undefined;
  };
}

declare namespace StandardSchemaV1 {
  // Standard Schema result types
  type Result<Output> = SuccessResult<Output> | FailureResult;
  
  interface SuccessResult<Output> {
    readonly value: Output;
    readonly issues?: undefined;
  }
  
  interface FailureResult {
    readonly issues: ReadonlyArray<Issue>;
  }
  
  // Issue interface for validation errors
  interface Issue {
    readonly message: string;
    readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
  }
  
  interface PathSegment {
    readonly key: PropertyKey;
  }
  
  interface Types<Input = unknown, Output = Input> {
    readonly input: Input;
    readonly output: Output;
  }
}

// Base configuration options
interface BaseOptions<
  TShared extends StandardSchemaDictionary,
  TExtends extends Array<Record<string, unknown>>
> {
  /** How to determine whether the app is running on the server or the client */
  isServer?: boolean;
  /** Shared variables available on both client and server */
  shared?: TShared;
  /** Extend presets */
  extends?: TExtends;
  /** Called when validation fails */
  onValidationError?: (issues: readonly StandardSchemaV1.Issue[]) => never;
  /** Called when a server-side environment variable is accessed on the client */
  onInvalidAccess?: (variable: string) => never;
  /** Whether to skip validation of environment variables */
  skipValidation?: boolean;
  /** Treat empty strings as undefined for better default value handling */
  emptyStringAsUndefined?: boolean;
}

// Loose runtime environment options
interface LooseOptions<
  TShared extends StandardSchemaDictionary,
  TExtends extends Array<Record<string, unknown>>
> extends BaseOptions<TShared, TExtends> {
  runtimeEnvStrict?: never;
  /** Runtime environment object - usually process.env */
  runtimeEnv: Record<string, string | boolean | number | undefined>;
}

// Strict runtime environment options  
interface StrictOptions<
  TPrefix extends string | undefined,
  TServer extends StandardSchemaDictionary,
  TClient extends StandardSchemaDictionary,
  TShared extends StandardSchemaDictionary,
  TExtends extends Array<Record<string, unknown>>
> extends BaseOptions<TShared, TExtends> {
  /** Strict runtime environment that enforces all variables are specified */
  runtimeEnvStrict: Record<string, string | boolean | number | undefined>;
  runtimeEnv?: never;
}

// Client-side configuration options
interface ClientOptions<
  TPrefix extends string | undefined,
  TClient extends StandardSchemaDictionary
> {
  /** Prefix for client-side variables (e.g., NEXT_PUBLIC_) */
  clientPrefix: TPrefix;
  /** Client-side environment variable schemas */
  client: Partial<Record<keyof TClient, TClient[keyof TClient]>>;
}

// Server-side configuration options
interface ServerOptions<
  TPrefix extends string | undefined,
  TServer extends StandardSchemaDictionary
> {
  /** Server-side environment variable schemas */
  server: Partial<Record<keyof TServer, TServer[keyof TServer]>>;
}

// Schema creation options
interface CreateSchemaOptions<
  TServer extends StandardSchemaDictionary,
  TClient extends StandardSchemaDictionary,
  TShared extends StandardSchemaDictionary,
  TFinalSchema extends StandardSchemaV1<{}, {}>
> {
  /** Custom function to combine the schemas */
  createFinalSchema?: (
    shape: TServer & TClient & TShared,
    isServer: boolean
  ) => TFinalSchema;
}

// Combined server/client options
type ServerClientOptions<
  TPrefix extends string | undefined,
  TServer extends StandardSchemaDictionary,  
  TClient extends StandardSchemaDictionary
> =
  | (ClientOptions<TPrefix, TClient> & ServerOptions<TPrefix, TServer>)
  | (ServerOptions<TPrefix, TServer> & Impossible<ClientOptions<never, never>>)
  | (ClientOptions<TPrefix, TClient> & Impossible<ServerOptions<never, never>>);

// Environment creation options
type EnvOptions<
  TPrefix extends string | undefined,
  TServer extends StandardSchemaDictionary,
  TClient extends StandardSchemaDictionary,
  TShared extends StandardSchemaDictionary,
  TExtends extends Array<Record<string, unknown>>,
  TFinalSchema extends StandardSchemaV1<{}, {}>
> = (
  | (LooseOptions<TShared, TExtends> & ServerClientOptions<TPrefix, TServer, TClient>)
  | (StrictOptions<TPrefix, TServer, TClient, TShared, TExtends> & ServerClientOptions<TPrefix, TServer, TClient>)
) & CreateSchemaOptions<TServer, TClient, TShared, TFinalSchema>;

// Core creation types
type CreateEnv<
  TFinalSchema extends StandardSchemaV1<{}, {}>,
  TExtends extends Array<Record<string, unknown>>
> = Readonly<Simplify<Reduce<[StandardSchemaV1.InferOutput<TFinalSchema>, ...TExtends]>>>;

type DefaultCombinedSchema<
  TServer extends StandardSchemaDictionary,
  TClient extends StandardSchemaDictionary,
  TShared extends StandardSchemaDictionary
> = StandardSchemaV1<
  {},
  UndefinedOptional<StandardSchemaDictionary.InferOutput<TServer & TClient & TShared>>
>;

// Utility types
type ErrorMessage<T extends string> = T;
type Simplify<T> = { [P in keyof T]: T[P] } & {};
type UndefinedOptional<T> = Partial<Pick<T, PossiblyUndefinedKeys<T>>> & Omit<T, PossiblyUndefinedKeys<T>>;
type PossiblyUndefinedKeys<T> = {
  [K in keyof T]: undefined extends T[K] ? K : never;
}[keyof T];
type Impossible<T extends Record<string, any>> = Partial<Record<keyof T, never>>;
type Reduce<
  TArr extends Record<string, unknown>[],
  TAcc = object
> = TArr extends []
  ? TAcc
  : TArr extends [infer Head, ...infer Tail]
    ? Tail extends Record<string, unknown>[]
      ? Mutable<Head> & Omit<Reduce<Tail, TAcc>, keyof Head>
      : never
    : never;
type Mutable<T> = T extends Readonly<infer U> ? U : T;