Created
November 18, 2025 14:43
-
-
Save Yotamho/9a05f8c41a8959168cc2b274b0585b1c to your computer and use it in GitHub Desktop.
A helper that converts a Zod discriminated union into a Payload CMS "blocks" field
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { z, ZodLiteral, type ZodObject } from 'zod/v4' | |
| import type { Field as PayloadField, Block } from 'payload' | |
| type BlocksField = Extract<PayloadField, { type: 'blocks' }> | |
| /** | |
| * Convert a Zod discriminated union into a Payload "blocks" field. | |
| * Assumptions (not validated): | |
| * - Each option is: { [discriminator]: z.literal('<slug>'), body: z.object({ [k]: z.string() }) } | |
| * - "body" keys are non-optional strings only. | |
| * - The discriminator is a non-optional string literal, and its key name is provided. | |
| */ | |
| export function zodDUToBlocksField( | |
| name: string, | |
| discriminator: string, | |
| du: z.ZodDiscriminatedUnion<readonly ZodObject<Record<string, z.ZodType>>[]>, | |
| ): BlocksField { | |
| // Zod v3 exposes `options` publicly (array or Map depending on version). | |
| const optionObjs: ZodObject<Record<string, z.ZodType>>[] = Array.isArray(du.options) | |
| ? du.options | |
| : [...du.options.values()] | |
| const blocks: Block[] = optionObjs.map((opt) => { | |
| // 1) slug from the discriminator literal | |
| const disc = opt.shape[discriminator] as ZodLiteral<string> | |
| const slug = disc.value | |
| // 2) fields from body object (strings only, per assumption) | |
| const body = opt.shape.body as ZodObject<Record<string, z.ZodString>> | |
| const fields: PayloadField[] = Object.keys(body.shape).map( | |
| (key) => | |
| ({ | |
| name: key, | |
| type: 'text', | |
| required: true, | |
| }) as const satisfies PayloadField, | |
| ) | |
| return { | |
| slug, | |
| labels: { singular: slug, plural: `${slug}s` }, | |
| fields, | |
| } | |
| }) | |
| return { | |
| name, | |
| type: 'blocks', | |
| required: true, | |
| minRows: 1, | |
| blocks, | |
| } as const | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment