Skip to content

Zod integration

This API is available since Optique 0.7.0.

The @optique/zod package provides seamless integration with Zod, enabling you to use Zod schemas for validating command-line arguments. This allows you to leverage Zod's powerful validation capabilities and reuse existing schemas across your CLI and application code.

deno add jsr:@optique/zod zod
npm add @optique/zod zod
pnpm add @optique/zod zod
yarn add @optique/zod zod
bun add @optique/zod zod

Basic usage

The zod() function creates a value parser from any Zod schema:

import { 
zod
} from "@optique/zod";
import {
z
} from "zod";
// Email validation const
email
=
zod
(
z
.
string
().
email
(), {
placeholder
: "" });
// Port number with range validation const
port
=
zod
(
z
.
coerce
.
number
().
int
().
min
(1024).
max
(65535), {
placeholder
: 1024 });
// Enum choices const
logLevel
=
zod
(
z
.
enum
(["debug", "info", "warn", "error"]), {
placeholder
: "debug" });

String coercion

CLI arguments are always strings, so use z.coerce for non-string types:

// ✅ Correct: Use z.coerce for numbers
const 
age
=
zod
(
z
.
coerce
.
number
().
int
().
min
(0), {
placeholder
: 0 });
// ❌ Won't work: z.number() expects actual numbers, not strings const
num
=
zod
(
z
.
number
(), {
placeholder
: 0 });

NOTE

Both z.boolean() and z.coerce.boolean() are handled specially: instead of rejecting CLI strings or applying JavaScript truthiness semantics, Optique accepts CLI-friendly literals (true/false, 1/0, yes/no, on/off, case-insensitive).

Transformations

Zod's transformation capabilities work seamlessly with Optique:

// Parse and transform to Date
const 
startDate
=
zod
(
z
.
string
().
transform
((
s
) => new
Date
(
s
)), {
placeholder
: new
Date
(0) });
// Transform to uppercase const
name
=
zod
(
z
.
string
().
transform
((
s
) =>
s
.
toUpperCase
()), {
placeholder
: "" });

Custom error messages

Customize error messages for better user experience:

const 
email
=
zod
(
z
.
string
().
email
(), {
placeholder
: "",
metavar
: "EMAIL",
errors
: {
zodError
: (
error
,
input
) =>
message
`Please provide a valid email address, got ${
input
}.`
} });

Integration with Optique

Zod parsers work seamlessly with all Optique features:

import { 
object
} from "@optique/core/constructs";
import {
option
,
argument
} from "@optique/core/primitives";
import {
zod
} from "@optique/zod";
import {
z
} from "zod";
const
config
=
object
({
email
:
option
("--email",
zod
(
z
.
string
().
email
(), {
placeholder
: "" })),
port
:
option
("-p", "--port",
zod
(
z
.
coerce
.
number
().
int
().
min
(1024).
max
(65535), {
placeholder
: 1024 })),
logLevel
:
option
("--log-level",
zod
(
z
.
enum
(["debug", "info", "warn", "error"]), {
placeholder
: "debug" })),
startDate
:
argument
(
zod
(
z
.
string
().
transform
((
s
) => new
Date
(
s
)), {
placeholder
: new
Date
(0) })),
});

Version compatibility

The @optique/zod package supports both Zod v3 (3.25.0+) and Zod v4 (4.0.0+):

  • Zod v3: Uses standard error messages from error.issues[0].message
  • Zod v4: Automatically uses prettifyError() when available for better error formatting

Limitations

  • Async refinements not supported: Since Optique's parsing is synchronous, async Zod features like refine(async ...) cannot be used. Async boolean schemas are detected when a valid boolean literal is parsed ("true", "false", etc.) and throw a TypeError; unrecognized inputs like "maybe" return a normal validation error instead. Perform async validation after parsing if needed.

  • Boolean parsing in unions: The CLI-friendly boolean parsing (accepting true/false, 1/0, yes/no, on/off) applies only when the entire schema is recognized as a boolean type. For unions that are not recognized as wholly boolean, arm precedence is preserved and parsing follows Zod's native union/coercion behavior.

The Zod integration provides a powerful way to reuse validation logic across your entire application while maintaining full type safety and excellent error messages.