Beta You're reading the docs for Kubb v5, which is currently in beta. View the stable v4 docs
Skip to content

Migration: @kubb/plugin-zod

Part of the v4 → v5 migration guide. See the full option reference in @kubb/plugin-zod.

Zod v3 no longer supported

The version option ('3' | '4') is removed. v5 always generates Zod v4 schemas.

Upgrade your zod dependency:

shell
bun add zod@^4
shell
pnpm add zod@^4
shell
npm install zod@^4
shell
yarn add zod@^4

Removed: mapper

Use macros or printer instead.

Removed: paramsCasing

v4 kubb.config.ts
typescript
pluginZod({ paramsCasing: 'camelcase' })

Properties inside the generated path, query, and header schemas are now always camelCase, so drop the option. The request still uses the original spec names, and Kubb writes the mapping for you.

Generated output
typescript
// OpenAPI spec uses: pet_id
export const getPetPathParamsSchema = z.object({ petId: z.string() }) // was { pet_id: z.string() }

Renamed: transformers.name

resolver.resolveSchemaName replaces transformers.name.

Moved to adapterOas

dateType, integerType, unknownType, and emptySchemaType moved to adapterOas. See Migration: @kubb/adapter-oas.

New: mini

Generate the functional syntax of Zod Mini for better tree-shaking. When mini: true, importPath defaults to 'zod/mini'.

kubb.config.ts
typescript
import {  } from 'kubb'
import {  } from '@kubb/plugin-zod'

export default ({
  : { : './petstore.yaml' },
  : { : './src/gen' },
  : [({ : true })],
})

New: regexType

Pick how an OpenAPI pattern is emitted inside .regex(...). The default 'literal' keeps a regex literal, while 'constructor' switches to the RegExp constructor. Use the constructor form when a regex literal trips up your build pipeline or when you need the pattern as a string.

kubb.config.ts
typescript
import {  } from 'kubb'
import {  } from '@kubb/plugin-zod'

export default ({
  : { : './petstore.yaml' },
  : { : './src/gen' },
  : [({ : 'constructor' })],
})
Generated output
typescript
slug: z.string().regex(/^[a-z]+$/),
slug: z.string().regex(new RegExp('^[a-z]+$')),

Changed: inferred type names end with Type

With inferred: true, the z.infer<typeof schema> alias now carries a SchemaType suffix. petSchema exports PetSchemaType instead of PetSchema.

In v4 the schema value and its inferred type differed only by casing (petSchema and PetSchema). An all-uppercase name such as SUV, URL, or API produced the same identifier for both, so the barrel re-exported it twice and failed with TS2300: Duplicate identifier. The Type suffix keeps the value and type apart at any casing.

zod/petSchema.ts
typescript
export const petSchema = z.object({
  name: z.string(),
  status: z.enum(['available', 'pending', 'sold']).optional(),
})

export type PetSchemaType = z.infer<typeof petSchema>
export type PetSchema = z.infer<typeof petSchema>

Update any imports that referenced the old name:

Update imports
typescript
import type { PetSchemaType } from './gen/zod/petSchema.ts'
import type { PetSchema } from './gen/zod/petSchema.ts'

Generated output

Chained syntax instead of functional wrappers

v5 prefers the chained Zod 4 syntax. .optional() sits at the end of the chain, right before .describe().

Generated output
typescript
id: z.optional(z.int()),
shipDate: z.optional(z.iso.datetime()),
status: z.optional(z.enum(['placed', 'approved']).describe('Order Status')),
id: z.int().optional(),
shipDate: z.iso.datetime().optional(),
status: z.enum(['placed', 'approved']).optional().describe('Order Status'),

The functional form (z.optional(...)) is now reserved for mini: true output, which lives in its own output.path.

Self-referencing getters only for true cycles

v4 wrapped almost every nested ref in a getter. v5 does so only when the schema is truly circular, meaning it references itself or its parent.

Diff
diff
- get category() {
-   return categorySchema.optional()
- },
- get tags() {
-   return z.array(tagSchema).optional()
- },
+ category: categorySchema.optional(),
+ tags: z.array(tagSchema).optional(),
  get parent() {
    return z.array(petSchema).optional()
  },