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:
bun add zod@^4pnpm add zod@^4npm install zod@^4yarn add zod@^4Removed: mapper
Use macros or printer instead.
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, which tree-shakes better. When mini: true, importPath defaults to 'zod/mini'.
import { } from 'kubb'
import { } from '@kubb/plugin-zod'
export default ({
: { : './petstore.yaml' },
: { : './src/gen' },
: [({ : true })],
})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 to compile with TS2300: Duplicate identifier. The Type suffix keeps the value and type apart no matter the casing.
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:
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() always sits at the end of the chain, right before .describe().
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 configured output.path.
Self-referencing getters only for true cycles
v4 wrapped almost every nested ref in a getter. v5 only does so when the schema is truly circular, meaning it references itself or its parent.
- 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()
},