Skip to content
Playground

Zod

You may want to use Zod validator because:

  • You can use Zod schema as a source of truth for the form value type (z.infer).
  • This is an easy way to add custom error messages

Installation

Terminal window
npm i @sjsf/zod-validator zod zod-to-json-schema json-schema-to-zod

References:

Example

<script lang="ts">
import { RawForm, ON_INPUT, type Schema } from "@sjsf/form";
import { createValidator } from "@sjsf/zod-validator";
import { zodToJsonSchema } from "zod-to-json-schema";
import { z } from "zod";
import { createCustomForm } from "@/components/custom-form";
import { initialValue, uiSchema } from "./_shared";
const schema = z.object({
id: z
.string()
.regex(new RegExp("\\d+"), "Must be a number")
.min(8)
.optional(),
active: z.boolean().optional(),
skills: z.array(z.string().min(5)).min(4).optional(),
multipleChoicesList: z
.array(z.enum(["foo", "bar", "fuzz"]))
.max(2)
.optional(),
});
type Value = z.infer<typeof schema>;
const validator = createValidator({ schema, uiSchema });
const form = createCustomForm({
schema: zodToJsonSchema(schema, { errorMessages: true }) as Schema,
uiSchema,
validator,
fieldsValidationMode: ON_INPUT,
initialValue: initialValue as Value,
});
</script>
<RawForm {form} novalidate />
<pre>{JSON.stringify(form.value, null, 2)}</pre>

Caveats

  • If you using this library only for full form validation (without fields validation mode) and your form does not have and oneOf, anyOf, dependencies if keywords or recursive references, you can use this library pretty safely.

  • If you are using fields validation mode then starting from this point internally we start to use json-schema-to-zod. So first of all, please read this warning about using this library at runtime. However, I believe you are still safe as only small leafy bits of the circuit are transformed with this approach.

  • If you use any conditional keyword or recursive references it means with a high probability something can go wrong. If you have problems with this approach, try using zod with a different validator using the augment submodule. So in this case zod validator will be used only for full form validation.

import { createValidator } from "@sjsf/other-validator";
import { withZod } from "@sjsf/zod-integration/augment";
const validator = withZod(createValidator(), { schema });

Async validation

This validator supports async validation but only for the full form validation.

import { createValidator } from "@sjsf/zod-validator";
const validator = createValidator({
async: true,
schema,
});

API

import type { ZodSchema } from "zod";
import type { Schema, UiSchemaRoot, Config } from "@sjsf/form";
import type { Validator } from "@sjsf/zod-validator";
interface ValidatorOptions<Async extends boolean> {
async: Async;
schema: ZodSchema;
/** @default {} */
uiSchema?: UiSchemaRoot;
/** @default DEFAULT_ID_PREFIX */
idPrefix?: string;
/** @default DEFAULT_ID_SEPARATOR */
idSeparator?: string;
/** @default makeZodSchemaFactory() */
zodSchemaFactory?: (schema: Schema, rootSchema: Schema) => ZodSchema;
/** @default makeFieldZodSchemaFactory() */
fieldZodSchemaFactory?: (config: Config) => ZodSchema;
}
type ValidatorFactoryOptions<Async extends boolean> = Omit<
ValidatorOptions<Async>,
"async"
> & {
/** @default false */
async?: Async;
};
function createValidator<Async extends boolean = false>(
options?: ValidatorFactoryOptions<Async>
): Validator<Async>;