0
# @t3-oss/env-nextjs
1
2
@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.
3
4
## Package Information
5
6
- **Package Name**: @t3-oss/env-nextjs
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install @t3-oss/env-nextjs`
10
11
## Core Imports
12
13
```typescript
14
import { createEnv } from "@t3-oss/env-nextjs";
15
```
16
17
For CommonJS:
18
19
```javascript
20
const { createEnv } = require("@t3-oss/env-nextjs");
21
```
22
23
## Basic Usage
24
25
```typescript
26
import { createEnv } from "@t3-oss/env-nextjs";
27
import { z } from "zod";
28
29
export const env = createEnv({
30
// Server-side environment variables (not available on client)
31
server: {
32
DATABASE_URL: z.string().url(),
33
OPEN_AI_API_KEY: z.string().min(1),
34
},
35
// Client-side environment variables (requires NEXT_PUBLIC_ prefix)
36
client: {
37
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
38
},
39
// Shared environment variables (available on both server and client)
40
shared: {
41
NODE_ENV: z.enum(["development", "test", "production"]),
42
},
43
// Runtime environment values
44
runtimeEnv: {
45
DATABASE_URL: process.env.DATABASE_URL,
46
OPEN_AI_API_KEY: process.env.OPEN_AI_API_KEY,
47
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
48
NODE_ENV: process.env.NODE_ENV,
49
},
50
});
51
52
// Access environment variables with full type safety
53
console.log(env.DATABASE_URL); // string
54
console.log(env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY); // string
55
console.log(env.NODE_ENV); // "development" | "test" | "production"
56
```
57
58
## Architecture
59
60
@t3-oss/env-nextjs is built around several key components:
61
62
- **Core Function**: `createEnv` function that creates typesafe environment variable schemas with Next.js-specific configurations
63
- **Client Prefix Enforcement**: Automatic validation that client-side variables use the required `NEXT_PUBLIC_` prefix
64
- **Runtime Environment Management**: Support for both traditional `runtimeEnv` and experimental `experimental__runtimeEnv` modes
65
- **Type Safety**: Full TypeScript integration with compile-time type checking and runtime validation
66
- **Standard Schema Compatibility**: Works with any Standard Schema-compliant validator (Zod, Valibot, Arktype)
67
- **Preset System**: Pre-configured environment variable schemas for popular hosting platforms and services
68
69
## Capabilities
70
71
### Environment Schema Creation
72
73
Core functionality for creating typesafe environment variable schemas with Next.js-specific validation rules and runtime compatibility enforcement.
74
75
```typescript { .api }
76
function createEnv<
77
TServer extends StandardSchemaDictionary = NonNullable<unknown>,
78
TClient extends Record<`NEXT_PUBLIC_${string}`, StandardSchemaV1> = NonNullable<unknown>,
79
TShared extends StandardSchemaDictionary = NonNullable<unknown>,
80
const TExtends extends Array<Record<string, unknown>> = [],
81
TFinalSchema extends StandardSchemaV1<{}, {}> = DefaultCombinedSchema<TServer, TClient, TShared>
82
>(opts: Options<TServer, TClient, TShared, TExtends, TFinalSchema>): CreateEnv<TFinalSchema, TExtends>;
83
84
type Options<
85
TServer extends StandardSchemaDictionary,
86
TClient extends Record<`NEXT_PUBLIC_${string}`, StandardSchemaV1>,
87
TShared extends StandardSchemaDictionary,
88
TExtends extends Array<Record<string, unknown>>,
89
TFinalSchema extends StandardSchemaV1<{}, {}>
90
> = Omit<
91
StrictOptions<"NEXT_PUBLIC_", TServer, TClient, TShared, TExtends> &
92
ServerClientOptions<"NEXT_PUBLIC_", TServer, TClient> &
93
CreateSchemaOptions<TServer, TClient, TShared, TFinalSchema>,
94
"runtimeEnvStrict" | "runtimeEnv" | "clientPrefix"
95
> & (
96
| {
97
runtimeEnv: StrictOptions<"NEXT_PUBLIC_", TServer, TClient, TShared, TExtends>["runtimeEnvStrict"];
98
experimental__runtimeEnv?: never;
99
}
100
| {
101
runtimeEnv?: never;
102
experimental__runtimeEnv: Record<
103
| {
104
[TKey in keyof TClient]: TKey extends `NEXT_PUBLIC_${string}` ? TKey : never;
105
}[keyof TClient]
106
| {
107
[TKey in keyof TShared]: TKey extends string ? TKey : never;
108
}[keyof TShared],
109
string | boolean | number | undefined
110
>;
111
}
112
);
113
```
114
115
[Environment Schema Creation](./core.md)
116
117
### Zod Presets
118
119
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.
120
121
```typescript { .api }
122
// Example preset functions
123
function vercel(): Readonly<VercelEnv>;
124
function railway(): Readonly<RailwayEnv>;
125
function render(): Readonly<RenderEnv>;
126
function netlify(): Readonly<NetlifyEnv>;
127
```
128
129
[Zod Presets](./presets-zod.md)
130
131
### Arktype Presets
132
133
Pre-configured environment variable schemas using Arktype validators. Provides the same platform presets as Zod but using Arktype's type system for validation.
134
135
```typescript { .api }
136
// Example preset functions
137
function vercel(): Readonly<VercelEnv>;
138
function railway(): Readonly<RailwayEnv>;
139
function render(): Readonly<RenderEnv>;
140
function netlify(): Readonly<NetlifyEnv>;
141
```
142
143
[Arktype Presets](./presets-arktype.md)
144
145
### Valibot Presets
146
147
Pre-configured environment variable schemas using Valibot validators. Provides the same platform presets as Zod and Arktype but using Valibot's validation system.
148
149
```typescript { .api }
150
// Example preset functions
151
function vercel(): Readonly<VercelEnv>;
152
function railway(): Readonly<RailwayEnv>;
153
function render(): Readonly<RenderEnv>;
154
function netlify(): Readonly<NetlifyEnv>;
155
```
156
157
[Valibot Presets](./presets-valibot.md)
158
159
## Types
160
161
```typescript { .api }
162
// Client prefix type
163
type ClientPrefix = "NEXT_PUBLIC_";
164
165
// Standard schema types (from @t3-oss/env-core)
166
interface StandardSchemaDictionary {
167
[key: string]: StandardSchemaV1;
168
}
169
170
interface StandardSchemaV1<Input = any, Output = Input> {
171
readonly "~standard": {
172
readonly version: 1;
173
readonly vendor: string;
174
readonly validate: (value: unknown) => StandardSchemaV1.Result<Output> | Promise<StandardSchemaV1.Result<Output>>;
175
readonly types?: StandardSchemaV1.Types<Input, Output> | undefined;
176
};
177
}
178
179
declare namespace StandardSchemaV1 {
180
// Standard Schema result types
181
type Result<Output> = SuccessResult<Output> | FailureResult;
182
183
interface SuccessResult<Output> {
184
readonly value: Output;
185
readonly issues?: undefined;
186
}
187
188
interface FailureResult {
189
readonly issues: ReadonlyArray<Issue>;
190
}
191
192
// Issue interface for validation errors
193
interface Issue {
194
readonly message: string;
195
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
196
}
197
198
interface PathSegment {
199
readonly key: PropertyKey;
200
}
201
202
interface Types<Input = unknown, Output = Input> {
203
readonly input: Input;
204
readonly output: Output;
205
}
206
}
207
208
// Base configuration options
209
interface BaseOptions<
210
TShared extends StandardSchemaDictionary,
211
TExtends extends Array<Record<string, unknown>>
212
> {
213
/** How to determine whether the app is running on the server or the client */
214
isServer?: boolean;
215
/** Shared variables available on both client and server */
216
shared?: TShared;
217
/** Extend presets */
218
extends?: TExtends;
219
/** Called when validation fails */
220
onValidationError?: (issues: readonly StandardSchemaV1.Issue[]) => never;
221
/** Called when a server-side environment variable is accessed on the client */
222
onInvalidAccess?: (variable: string) => never;
223
/** Whether to skip validation of environment variables */
224
skipValidation?: boolean;
225
/** Treat empty strings as undefined for better default value handling */
226
emptyStringAsUndefined?: boolean;
227
}
228
229
// Loose runtime environment options
230
interface LooseOptions<
231
TShared extends StandardSchemaDictionary,
232
TExtends extends Array<Record<string, unknown>>
233
> extends BaseOptions<TShared, TExtends> {
234
runtimeEnvStrict?: never;
235
/** Runtime environment object - usually process.env */
236
runtimeEnv: Record<string, string | boolean | number | undefined>;
237
}
238
239
// Strict runtime environment options
240
interface StrictOptions<
241
TPrefix extends string | undefined,
242
TServer extends StandardSchemaDictionary,
243
TClient extends StandardSchemaDictionary,
244
TShared extends StandardSchemaDictionary,
245
TExtends extends Array<Record<string, unknown>>
246
> extends BaseOptions<TShared, TExtends> {
247
/** Strict runtime environment that enforces all variables are specified */
248
runtimeEnvStrict: Record<string, string | boolean | number | undefined>;
249
runtimeEnv?: never;
250
}
251
252
// Client-side configuration options
253
interface ClientOptions<
254
TPrefix extends string | undefined,
255
TClient extends StandardSchemaDictionary
256
> {
257
/** Prefix for client-side variables (e.g., NEXT_PUBLIC_) */
258
clientPrefix: TPrefix;
259
/** Client-side environment variable schemas */
260
client: Partial<Record<keyof TClient, TClient[keyof TClient]>>;
261
}
262
263
// Server-side configuration options
264
interface ServerOptions<
265
TPrefix extends string | undefined,
266
TServer extends StandardSchemaDictionary
267
> {
268
/** Server-side environment variable schemas */
269
server: Partial<Record<keyof TServer, TServer[keyof TServer]>>;
270
}
271
272
// Schema creation options
273
interface CreateSchemaOptions<
274
TServer extends StandardSchemaDictionary,
275
TClient extends StandardSchemaDictionary,
276
TShared extends StandardSchemaDictionary,
277
TFinalSchema extends StandardSchemaV1<{}, {}>
278
> {
279
/** Custom function to combine the schemas */
280
createFinalSchema?: (
281
shape: TServer & TClient & TShared,
282
isServer: boolean
283
) => TFinalSchema;
284
}
285
286
// Combined server/client options
287
type ServerClientOptions<
288
TPrefix extends string | undefined,
289
TServer extends StandardSchemaDictionary,
290
TClient extends StandardSchemaDictionary
291
> =
292
| (ClientOptions<TPrefix, TClient> & ServerOptions<TPrefix, TServer>)
293
| (ServerOptions<TPrefix, TServer> & Impossible<ClientOptions<never, never>>)
294
| (ClientOptions<TPrefix, TClient> & Impossible<ServerOptions<never, never>>);
295
296
// Environment creation options
297
type EnvOptions<
298
TPrefix extends string | undefined,
299
TServer extends StandardSchemaDictionary,
300
TClient extends StandardSchemaDictionary,
301
TShared extends StandardSchemaDictionary,
302
TExtends extends Array<Record<string, unknown>>,
303
TFinalSchema extends StandardSchemaV1<{}, {}>
304
> = (
305
| (LooseOptions<TShared, TExtends> & ServerClientOptions<TPrefix, TServer, TClient>)
306
| (StrictOptions<TPrefix, TServer, TClient, TShared, TExtends> & ServerClientOptions<TPrefix, TServer, TClient>)
307
) & CreateSchemaOptions<TServer, TClient, TShared, TFinalSchema>;
308
309
// Core creation types
310
type CreateEnv<
311
TFinalSchema extends StandardSchemaV1<{}, {}>,
312
TExtends extends Array<Record<string, unknown>>
313
> = Readonly<Simplify<Reduce<[StandardSchemaV1.InferOutput<TFinalSchema>, ...TExtends]>>>;
314
315
type DefaultCombinedSchema<
316
TServer extends StandardSchemaDictionary,
317
TClient extends StandardSchemaDictionary,
318
TShared extends StandardSchemaDictionary
319
> = StandardSchemaV1<
320
{},
321
UndefinedOptional<StandardSchemaDictionary.InferOutput<TServer & TClient & TShared>>
322
>;
323
324
// Utility types
325
type ErrorMessage<T extends string> = T;
326
type Simplify<T> = { [P in keyof T]: T[P] } & {};
327
type UndefinedOptional<T> = Partial<Pick<T, PossiblyUndefinedKeys<T>>> & Omit<T, PossiblyUndefinedKeys<T>>;
328
type PossiblyUndefinedKeys<T> = {
329
[K in keyof T]: undefined extends T[K] ? K : never;
330
}[keyof T];
331
type Impossible<T extends Record<string, any>> = Partial<Record<keyof T, never>>;
332
type Reduce<
333
TArr extends Record<string, unknown>[],
334
TAcc = object
335
> = TArr extends []
336
? TAcc
337
: TArr extends [infer Head, ...infer Tail]
338
? Tail extends Record<string, unknown>[]
339
? Mutable<Head> & Omit<Reduce<Tail, TAcc>, keyof Head>
340
: never
341
: never;
342
type Mutable<T> = T extends Readonly<infer U> ? U : T;
343
```