Zod

Validate environment variables with your existing Zod schemas.

Edit on GitHub

The Zod schemas you already write for other inputs can validate the environment too. ArkEnv runs them against that environment and coerces string values before they hit Zod.

Zod 4.2+ embeds Standard JSON Schema on schema objects, which is what makes "3000" and "true" coerce. Classic Zod uses the root @arkenv/standard import. There is no @arkenv/standard/zod subpath. @arkenv/standard/valibot and @arkenv/standard/zod-mini exist only to bind JSON Schema converters those libraries keep off the value.

How the two engines differ is in Validators.

Installation

Install @arkenv/standard next to zod. @arkenv/standard has no runtime dependencies. Zod stays your dependency.

npm install @arkenv/standard zod

Define your schema

Put the Zod schemas you already write into env.ts. Each field is that schema. Import { env } where the process starts.

./env.ts
import  from "@arkenv/standard";
import * as  from "zod";

export const  = ({
  : .(),
  : .().(0).(65535).(3000),
  : .().(false),
  : .(.()).([]),
  : 
    .(["development", "production", "test"])
    .("development"),
});

Because Zod 4.2+ includes JSON Schema metadata, ArkEnv automatically converts "3000" to 3000, "true" to true, and comma-separated lists like "web, api" into string arrays before validation. You can use standard z.number() and z.boolean() directly without manual z.coerce helpers.

Defaults and optionals

Zod methods for defaults and optional fields behave consistently with ArkEnv:

MethodBehavior
.default(...)Fallback value used when the environment variable is missing
.optional()Allows the environment variable to remain undefined
emptyAsUndefined: trueConverts empty strings ("") to undefined so .default() triggers

Zod Mini

On Zod 4.2+ classic?

Skip this section. The root import already coerces. Zod Mini is the build that leaves JSON Schema off the schema object.

Zod Mini (import * as z from "zod/mini") omits embedded JSON Schema metadata from schema instances. Import @arkenv/standard/zod-mini so Mini z.number() and z.boolean() coerce without a callback:

./env.ts
import {  } from "@arkenv/standard/zod-mini";
import * as  from "zod/mini";

export const  = ({
  : .(),
  : .(),
});

TypeScript must use moduleResolution: "bundler" | "node16" | "nodenext". The root toJsonSchema callback remains available for mixed maps; see Coercion.

Zod v3

Zod v3 (including zod/v3 subpath exports in Zod 4) implements Standard Schema validation but lacks native JSON Schema metadata. You can enable automatic coercion by passing zod-to-json-schema through the toJsonSchema callback:

./env.ts
import  from "@arkenv/standard";
import {  } from "zod/v3";
import {  } from "zod-to-json-schema";

export const  = (
  {
    : .(),
    : .(),
  },
  {
    : (schema) =>
      (schema as ., {
        : "none",
      }),
  },
);

Use Zod 4.2+ when you can. It ships JSON Schema metadata, so you skip the toJsonSchema callback.

Next steps