Skip to content

arrayFieldToInputSchema

arrayFieldToInputSchema<TArrayType, TFieldMeta>(props): ZodType

Defined in: packages/core/src/fields/array/inputSchema.ts:92

Builds a Zod schema for validating an array field value in the admin form.

Wraps the nested item schema (built recursively via adminFieldToInputSchema) in z.array(…), then composes min/max item-count constraints onto the same chain built for required, rather than reassigning over it — a required field with min/max configured keeps every check. Required fields attach { error: "This field is required." } to the base z.array() call and add .min(1, "This field is required."), replacing the previous dead superRefine (it only ever checked for undefined/ null, which the previously-unconditional .default() had already substituted away before the refine ran, and it never checked emptiness at all) (CORE-1). Required fields never receive .default(); non-required fields keep .default(field.defaultValue ?? []).

min/max are independent of required, but only skip an empty array when the field is optional: an optional field’s empty/omitted value shouldn’t fail its own min, but a required field’s empty value is already invalid (via .min(1, requiredError) above), so its configured min/max still runs on [] too — otherwise a required field’s own custom min/max error message could never surface (an empty array is the only way to violate a min in the first place) and only the generic “This field is required.” would ever show. A non-empty array — required or not — is always checked against a configured min/max.

When props.data is given AND field.items (or anything nested in it) declares a condition, each array ITEM needs its own siblingData — unlike a group’s sub-fields, items are positional, not keyed, so there is no single data[key] to compute up front at schema-build time (Step 9). The base/outer item schema is built loose (z.unknown()) instead of the real item schema — enforcing required up front there would reject a condition-hidden field before the real, condition-aware per-item re-parse ever runs — and a superRefine re-parses every actual item against a fresh adminFieldToInputSchema({ field: field.items, data, siblingData: item }) — item IS the new siblingData, since an array item’s resolved value has no extra key to index through, mirroring how adminFieldToInputSchema already treats a missing key for item dispatch. Issues from that re-parse are re-added through ctx.addIssue with the item’s index prepended to the path, so a failure still names the right item/field for the admin form. Without conditions anywhere in field.items, the item schema stays exactly what it was — a single adminFieldToInputSchema({ field: field.items, ignoreRequired }) call, no superRefine, no added cost.

An object-shaped item (field.items.type === "group") additionally gets id: z.string().optional() intersected onto its schema (withItemId, below) — optional because an existing document predating this step may have items with no id yet (same reasoning as the Convex validator, array/validator.ts); prepareEdit’s assignArrayItemIds backfills one on every write regardless, so the client form never actually submits an id-less object item. .and(...), not groupFieldToInputSchema taking an extra parameter, because a group() used OUTSIDE an array must never gain an id slot — this intersection is scoped to array ITEMS only.

TArrayType extends ArrayType = string

TFieldMeta extends object = { }

Input props.

Record<string, unknown>

The full top-level document. Omitted skips condition evaluation for this array’s items entirely (today’s behavior).

ArrayField<string, TArrayType, TFieldMeta>

The resolved array field definition

boolean

Forwarded to the recursive adminFieldToInputSchema({ field: field.items }) call so a draft-lenient pass (getFieldsInputSchema’s ignoreRequired) reaches every item, not just this array’s own required check.

ZodType

A Zod array schema with item count constraints and optionality applied

const field = array({ items: text(), required: true })
arrayFieldToInputSchema({ field })
// → z.array(z.string(), { error: "This field is required." }).min(1, "This field is required.")
// Optional field, min/max still enforced once a value is supplied
const field = array({ items: number(), min: 2, max: 5 })
const schema = arrayFieldToInputSchema({ field })
schema.safeParse(undefined) // → success: [] (skips min/max — empty)
schema.safeParse([1]) // → fails: below min
schema.safeParse([1, 2]) // → success