Skip to content
Reference

Options

Configuration options for @kubb/plugin-zod.

Options for pluginZod.

OptionTypeDefaultDescription
outputOutput{ path: 'zod', barrel: { type: 'named' } }Where the generated files are written and exported
groupGroup—Split output into per-tag or per-path folders
importPathstringmini ? 'zod/mini' : 'zod'Module the generated files import z from
inferredbooleanfalseEmit a z.infer alias next to each schema
coercionboolean | { dates?: boolean, strings?: boolean, numbers?: boolean }falseCoerce input before validation
guidType'uuid' | 'guid''uuid'Validator for format: uuid properties
regexType'literal' | 'constructor''literal'How an OpenAPI pattern is written
compileboolean | { strict?: boolean }falseWrap schemas in z.compile for fast-path validation
minibooleanfalseGenerate Zod Mini schemas
typeGuardsboolean | { is?: boolean, assert?: boolean }falseGenerate is* type guards and assert* assertions
includeArray<Include>—Keep only operations that match
excludeArray<Exclude>[]Skip operations that match
overrideArray<Override>[]Apply different options per pattern
resolverResolverPatch<ResolverZod>—Customize generated names and file paths
macrosArray<Macro>—Rewrite AST nodes before printing
printer{ nodes?: PrinterZodNodes | PrinterZodMiniNodes }—Replace the handler for a schema type

output

Where the generated .ts files are written and how they are exported.

output.path

Folder where the plugin writes its files, resolved against the global output.path on defineConfig. For a single file, set output.mode: 'file' and give path an extension, such as 'zod.ts'.

Type:string
Default:'zod'

output.mode

How the plugin consolidates generated code into files.

  • 'file' writes everything into a single file, so output.path must include the file extension (for example 'zod.ts').
  • 'directory' writes one file per operation or schema under output.path.

Leave it unset and Kubb reads output.path: a name with an extension means one file, anything else a directory.

Type:'directory' | 'file'
Default:follows the shape of output.path

output.barrel

Toggle the export style and depth to see the generated barrels.

  • src/gen/
  • models/
  • Pet.ts
  • User.ts
  • clients/
  • pet/
  • getPetById.ts
  • store/
  • getInventory.ts
src/gen/index.ts
export { Pet, User } from './models'
export { getPetById, getInventory } from './clients'

Controls how the generated index.ts (barrel) re-exports the output. Accepts { type: 'named' } or { type: 'all' }, optionally with nested: true (for example { type: 'named', nested: true }) to write an index.ts in every subdirectory, or false to skip the barrel entirely. Kubb reads the plugin's own output.barrel first, falls back to config.output.barrel on defineConfig, and finally to false. Every generator plugin ships a default output that sets barrel: { type: 'named' }, but passing your own output replaces that object wholesale, so repeat barrel whenever you set output yourself.

output.banner

Text added to the top of every generated file, such as a license header or @ts-nocheck directive. Pass a string, or a function (meta: BannerMeta) => string that receives the document info (title, description, version, baseURL) and per-file context (filePath, baseName, isBarrel, isAggregation), so a directive can skip barrel files.

Text added to the bottom of every generated file (string or (meta: BannerMeta) => string), like banner but for closing comments. Pair banner: '/* eslint-disable */' with footer: '/* eslint-enable */' to scope a lint disable to the generated file.

group

Switch the mode to see where these operations land on disk.

clients/pet/
  • getPetById
  • addPet
clients/store/
  • getInventory
clients/order/
  • placeOrder
  • getOrderById
clients/user/
  • loginUser
group: { type: "tag" } splits the output by the operation tag, so placeOrder follows its order tag.

Splits generated files into subfolders by the operation's tag or URL path, each under {output.path}/{groupName}/. Without group, every file lands directly in output.path. It applies only to output.mode: 'directory'.

Combining group with output.mode: 'file' stops the build with a KUBB_INVALID_PLUGIN_OPTIONS error.

group.type

Property used to assign each operation to a group ('tag' | 'path'), required whenever group is set. An operation with no tag goes in the default group.

  • 'tag' uses the operation's first tag.
  • 'path' uses the first URL segment, such as pet for /pet/{petId}.

group.name

Function that turns a group key (first tag or path segment) into a folder or identifier name, used as the subdirectory under output.path and a suffix for aggregate files. For type: 'path', the default keeps the URL segment as-is instead of camelCasing.

Type:(context: { group: string }) => string
Default:'tag': ({ group }) => camelCase(group); 'path': the raw URL segment, uncased

importPath

Module specifier for the import { z } from '...' statement in every generated file, so you can re-export Zod from your own module. Defaults to 'zod', or 'zod/mini' when mini is on.

'zod' and 'zod/mini' import the z namespace (import * as z), but a custom module imports the named z export (import { z }), so re-export z from there.

inferred

Exports a z.infer<typeof schema> type alias next to every generated schema, so the schema is the single source of truth and you do not import types from @kubb/plugin-ts. The alias is the PascalCased schema name with a SchemaType suffix, so petSchema becomes PetSchemaType.

import * as z from 'zod'

export const petSchema = z.object({
  name: z.string(),
})

export type PetSchemaType = z.infer<typeof petSchema>

It also generates a ResponsesSchema per operation, the per-status responses record, with its inferred type. @kubb/plugin-fetch and @kubb/plugin-axios key their RequestResult on it when @kubb/plugin-ts is absent.

coercion

Wraps schemas in z.coerce so input is coerced before validation, for form data, query params, and similar string sources.

  • true coerces strings, numbers, and dates.
  • false (default) coerces nothing and validates strictly.
  • An object picks which primitives to coerce.

See Coercion for primitives.

z.coerce.string()
z.coerce.number()
z.coerce.date()

dates coerces only Date-typed fields (from dateType: 'date'). Fields kept as ISO strings (z.iso.date(), z.iso.datetime()) are never coerced.

format: time fields are never coerced either, because new Date() cannot parse a bare HH:mm:ss. With dateType.time: 'date', a time decodes into a Date on 1970-01-01 UTC and encodes back to HH:mm:ss (fractional seconds are dropped). For a real time-of-day type such as Temporal.PlainTime, see Encode a custom type on requests.

guidType

Validator used for OpenAPI properties with format: uuid.

  • 'uuid' (default) generates z.uuid(), a standard RFC 4122 UUID.
  • 'guid' generates z.guid(), which is looser and accepts Microsoft-style GUIDs.

regexType

Controls how an OpenAPI pattern is written inside .regex(...).

  • 'literal' (default) emits a regex literal, such as .regex(/^[a-z]+$/).
  • 'constructor' emits the RegExp constructor, such as .regex(new RegExp('^[a-z]+$')).

Use 'constructor' when a regex literal breaks your build or you need a string pattern.

compile

Wraps schemas in z.compile(...) to generate validation code instead of interpreting the schema on each call.

  • true compiles schemas using z.compile(...).
  • false (default) leaves schemas uncompiled.
  • { strict: true } passes { strict: true } to z.compile(...), which throws an error if any part of the schema cannot be compiled into flat JavaScript, preventing silent fallback to the interpreter.
compile requires Zod v4.5.0 or higher. Schemas with circular references (z.lazy) and bare $ref response aliases are automatically kept uncompiled to prevent runtime errors.
import * as z from 'zod'

export const petSchema = z.compile(
  z.object({
    id: z.number(),
    name: z.string(),
  }),
)

With { strict: true }:

import * as z from 'zod'

export const petSchema = z.compile(
  z.object({
    id: z.number(),
    name: z.string(),
  }),
  { strict: true },
)

mini

Switches code generation to Zod Mini, which uses the functional API (z.optional(z.string())) instead of the chainable one (z.string().optional()) so bundlers can tree-shake unused validators. mini: true also defaults importPath to 'zod/mini'.

import * as z from 'zod/mini'

z.optional(z.string())
z.nullable(z.number())
z.array(z.string()).check(z.minLength(1), z.maxLength(10))

typeGuards

The generated type guards and assertions require Zod v4.6.0 or higher.

Generates TypeScript type guards (is*) and assertion functions (assert*) for schemas using Zod v4's native validate API.

  • true: Generates both is<Schema> type guards and assert<Schema> assertion functions.
  • { is?: boolean; assert?: boolean }: Selectively enables type guards or assertions.
  • false (default): Generates only the Zod schemas.
pluginZod({
  typeGuards: true,
})

Emitted code:

src/gen/zod/petSchema.ts
import * as z from 'zod'

export const petSchema = z.object({
  id: z.int32(),
  name: z.string(),
})

export const isPet = (data: unknown): data is z.infer<typeof petSchema> => petSchema.validate(data)

export function assertPet(data: unknown): asserts data is z.infer<typeof petSchema> {
  if (!petSchema.validate(data)) {
    petSchema.parse(data)
  }
}

When inferred is true, the guards narrow to the generated schema type alias (e.g. PetSchemaType). When mini is true, they route through z.validate and z.parse.

Use the generated guard to filter unknown data, or assert a value before reading it:

usage.ts
import { isPet, assertPet } from './src/gen/zod/petSchema'

const items: unknown[] = []
const pets = items.filter(isPet)

export function processPayload(payload: unknown) {
  assertPet(payload)
  console.log(payload.name)
}

include

Generates only the operations and schemas that match at least one entry, and skips the rest. Each entry filters by tag, operationId, path, method, contentType, or schemaName, with a pattern that can be a string or a RegExp, both matched as a regular expression against the value. A string pattern is compiled with new RegExp(pattern), so it is not an exact match: pattern: 'pet' also matches 'petType' or 'superpet'.

Type definition
export type Include = {
  type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
  pattern: string | RegExp
}

exclude

Skips any operation or schema that matches at least one entry, the opposite of include. Entries use the same type and pattern fields as include, and when both options match an item, exclude wins.

When operations are excluded on a client plugin (@kubb/plugin-fetch or @kubb/plugin-axios), dependent plugins (@kubb/plugin-react-query, @kubb/plugin-vue-query, @kubb/plugin-swr, @kubb/plugin-mcp) skip generating hooks or handlers for those operations automatically, without requiring duplicate exclude configurations.

override

Applies different plugin options to operations that match a pattern. Each entry takes the same type and pattern as include, plus an options object that accepts any plugin option except override, so rules cannot nest. The first matching entry merges onto the plugin defaults, and later entries do not stack.

Type definition
export type Override = {
  type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
  pattern: string | RegExp
  options: Omit<Partial<Options>, 'override'>
}

When options such as returnType, output, or group are overridden on a client plugin (@kubb/plugin-fetch or @kubb/plugin-axios), dependent plugins (@kubb/plugin-react-query, @kubb/plugin-vue-query, @kubb/plugin-swr, @kubb/plugin-mcp) resolve and follow those per-operation options automatically.

For example, override: [{ type: 'tag', pattern: 'user', options: { coercion: true } }] coerces input only for the user tag.

resolver

Overrides generated file and symbol names. Omitted members keep the plugin's resolver defaults. See Override a resolver for the this context and how a patch layers over the default.

Inside a method this is the full resolver, so this.default.name(name) reuses the built-in casing.
Partial override
type ResolverZodPatch = {
  name?(name: string): string
  file?: {
    baseName?(params: { name: string; extname: string }): string
    path?(params: { baseName: string; output: Output }): string
  }
  schema?: {
    typeName?(name: string): string       // → 'PetSchemaType'
    type?(name: string): string           // → 'PetSchemaType'
    inputName?(name: string): string      // → 'orderInputSchema'
    inputTypeName?(name: string): string  // → 'OrderInputSchemaType'
    isName?(name: string): string         // → 'isPet'
    assertName?(name: string): string     // → 'assertPet'
  }
  param?: {
    name?(node: OperationNode, param: ParameterNode): string    // → 'deletePetPathPetIdSchema'
    path?(node: OperationNode, param: ParameterNode): string     // → 'deletePetPathSchema'
    query?(node: OperationNode, param: ParameterNode): string    // → 'findPetsByStatusQuerySchema'
    headers?(node: OperationNode, param: ParameterNode): string  // → 'deletePetHeadersSchema'
  }
  response?: {
    status?(node: OperationNode, statusCode: StatusCode): string // → 'listPetsStatus200Schema'
    body?(node: OperationNode): string                           // → 'createPetBodySchema'
    responses?(node: OperationNode): string                      // → 'listPetsResponsesSchema'
    response?(node: OperationNode): string                       // → 'listPetsResponseSchema'
    error?(node: OperationNode): string                          // → 'listPetsErrorSchema'
    options?(node: OperationNode): string                        // → 'ListPetsOptionsSchemaType'
  }
}

macros

Rewrites AST nodes before they are printed, without forking the generator. Each macro callback (such as schema or operation) receives the node and a context object, and returns a replacement or undefined to leave it as is. Omitted callbacks keep their defaults, and macros run in order, so a later one sees the output of an earlier one.

printer

Replaces the Zod handler for a schema type such as 'integer' or 'string', each returning the Zod expression as a string and targeting the Zod Mini printer when mini: true. Inside a handler, this.base(node) returns the built-in output to wrap and this.transform(node) recurses into nested nodes. See the printer guide.

import { pluginZod } from '@kubb/plugin-zod'

pluginZod({
  printer: {
    nodes: {
      integer() {
        return 'z.number()'
      },
      date() {
        return 'z.string().date()'
      },
    },
  },
})

A handler that reads this.options.direction ('decode' for responses, 'encode' for request bodies and parameters) and returns a different expression per direction registers a two-way conversion: the generator detects the difference and emits an ${name}InputSchema variant for request bodies to resolve to, including through a $ref. See Encode a custom type on requests.

Format and type mappings

@kubb/plugin-zod generates native Zod v4 schemas for standard OpenAPI types and formats:

OpenAPI Type / FormatStandard Zod OutputZod Mini OutputNotes
integerz.int()z.int()Coerces to z.coerce.number().int() when coercion.numbers is enabled
integer, format: int32z.int32()z.int32()32-bit signed integer
integer, format: uint32z.uint32()z.uint32()32-bit unsigned integer
integer, format: int64z.bigint()z.bigint()64-bit integer
string, format: byte / base64z.base64()z.base64()Base64 string validation
string, format: base64urlz.base64url()z.base64url()URL-safe base64 string validation
string, format: jwtz.jwt()z.jwt()JSON Web Token format
string, format: ulidz.ulid()z.ulid()ULID format
string, format: ibanz.iban()z.iban()International Bank Account Number
string, format: durationz.iso.duration()z.iso.duration()ISO 8601 duration format
string, format: uuidz.uuid() (or z.guid())z.uuid() (or z.guid())Configured via guidType
string, format: emailz.email()z.email()Email format
string, format: uri / urlz.url()z.url()URL format
string, format: ipv4 / ipv6z.ipv4() / z.ipv6()z.ipv4() / z.ipv6()IP address format
string, format: datez.iso.date()z.iso.date()ISO 8601 date
string, format: date-timez.iso.datetime()z.string()ISO 8601 date-time
string, format: timez.iso.time()z.iso.time()ISO 8601 time
object, additionalProperties: <schema>z.record(z.string(), schema)z.record(z.string(), schema)Dictionary with no fixed properties
object, additionalProperties: truez.looseObject(shape)z.looseObject(shape)Open/passthrough object allowing extra keys
object, propertyNames: <schema>z.record(keySchema, schema)z.record(keySchema, schema)Dynamic map with validated key schema (e.g. pattern, format)
object, propertyNames: <enum>z.partialRecord(enumSchema, schema)z.partialRecord(enumSchema, schema)Closed key schemas use partial record to avoid exhaustiveness

Dictionaries, open objects, and key schemas

OpenAPI 3.1 propertyNames validates dictionary keys. Open key schemas use z.record(keySchema, valueSchema), including regex, UUID, and length constraints. Closed keys, such as enums, literals, or unions of enums, use z.partialRecord so only present keys are validated. This preserves OpenAPI's partial semantics and infers Partial<Record<Keys, Value>>.

patternProperties combines key regex patterns into an alternation and emits z.record(z.string().regex(...), valueSchema). Zod Mini uses z.string().check(z.regex(...)) for the key schema.

An additionalProperties schema without fixed properties produces a dictionary. With fixed properties, it produces .catchall(valueSchema) to preserve that declared shape. additionalProperties: true emits z.looseObject(shape) to allow undeclared keys.