AutoForm
Schema Providers

Zod

@acp-autoform/zod supports Zod v3, Zod v4, and Zod Mini. The examples on this page use Zod v4.

Basic usage:

"use client";
import * as z from "zod";
import { ZodProvider, fieldConfig } from "@acp-autoform/zod";
import { AutoForm, FieldTypes } from "@acp-autoform/mui"; // use any UI library

// Define your form schema using zod
const formSchema = z.object({
  username: z
    .string({
      error: "Username is required.",
    })
    // You can use zod's built-in validation as normal
    .min(2, {
      message: "Username must be at least 2 characters.",
    }),

  password: z
    .string({
      error: "Password is required.",
    })
    // Use the "describe" method to set the label
    // If no label is set, the field name will be used
    // and un-camel-cased
    .describe("Your secure password")
    .min(8, {
      message: "Password must be at least 8 characters.",
    })
    // You can add additional config for how to render this field
    // using fieldConfig
    .check(
      fieldConfig<React.ReactNode, FieldTypes, any, { isImportant?: boolean }>({
        description: "We recommend to use a strong password.",
        inputProps: {
          type: "password",
        },
        customData: {
          // You can add custom data here
          isImportant: true,
        },
      }),
    ),

  favouriteNumber: z.coerce // When using numbers, you must use coerce
    .number({
      error: "Favourite number must be a number.",
    })
    .min(1, {
      message: "Favourite number must be at least 1.",
    })
    .max(10, {
      message: "Favourite number must be at most 10.",
    })
    .default(5) // You can set a default value
    .optional(),

  acceptTerms: z
    .boolean()
    .describe("Accept terms and conditions.")
    .check(
      z.refine((value) => value, "You must accept the terms and conditions."),
    ),

  // Date will show a date picker
  birthday: z.coerce.date().optional(),

  // Enum will show a select
  color: z.enum(["red", "green", "blue"]),

  // You can use sub-objects that will be rendered with their own title
  guestDetails: z.object({
    name: z.string(),
    age: z.coerce.number(),
  }),
});

const schemaProvider = new ZodProvider(formSchema);

function App() {
  return (
    <AutoForm
      schema={schemaProvider}
      onSubmit={(data) => {
        console.log(data);
      }}
      withSubmit
    />
  );
}

Zod configuration

Labels

You can use the describe method to set a label for each field. If no label is set, the field name will be used and un-camel-cased.

const formSchema = z.object({
  username: z.string().describe("Your username"),
  someValue: z.string(), // Will be "Some Value"
});

To add a description below the field see fieldConfig.

Coercion

When using numbers and dates, you should use coerce. This is because input elements may return a string that should automatically be converted.

const formSchema = z.object({
  favouriteNumber: z.coerce.number(),
});

Optional fields

By default, all fields are required. You can make a field optional by using the optional method.

const formSchema = z.object({
  username: z.string().optional(),
});

Default values

You can set a default value for a field using the default method.

const formSchema = z.object({
  favouriteNumber: z.number().default(5),
});

.default() acts as a fallback during validation: If a user clears the field, the default value comes back in the output. If you want pre-filled initial values that the user can freely clear, then use the defaultValues prop instead. See Zod .default().

If you want to set default value of date, convert it to Date first using new Date(val).

Select/Enums

AutoForm supports enum and nativeEnum to create select fields.

const formSchema = z.object({
  color: z.enum(["red", "green", "blue"]),
});

enum BreadTypes {
  // For TypeScript enums, the enum values (e.g. "White bread")
  // are displayed, validated and returned in output.
  White = "White bread",
  Brown = "Brown bread",
  Wholegrain = "Wholegrain bread",
}

const formSchema = z.object({
  bread: z.nativeEnum(BreadTypes),
});

If you want a select label to submit a different value, map the labels to values in a transform.

const nameId = {
  name1: "id1",
  name2: "id2",
} as const;

const formSchema = z.object({
  nameId: z
    .enum(Object.keys(nameId) as [string, ...string[]], {
      message: "Invalid name",
    })
    .transform((name) => nameId[name as keyof typeof nameId]),
});

Arrays

AutoForm supports arrays:

const formSchema = z.object({
  invitedGuests: z
    .array(
      // Define the fields for each item
      z.object({
        name: z.string(),
        age: z.coerce.number(),
      }),
    )
    // Optionally set a custom label - otherwise this will be inferred from the field name
    .describe("Guests invited to the party"),
  hobbies: z.array(z.string()),
});

Arrays are not supported as the root element of the form schema.

You also can set default value of an array using .default(), but please make sure the array element has same structure with the schema.

const formSchema = z.object({
  guestListName: z.string(),
  invitedGuests: z
    .array(
      // Define the fields for each item
      z.object({
        name: z.string(),
        age: z.coerce.number(),
      }),
    )
    .describe("Guests invited to the party")
    .default([
      {
        name: "John",
        age: 24,
      },
      {
        name: "Jane",
        age: 20,
      },
    ]),
});

Sub-objects

You may use sub-objects to group fields together. These will be rendered with their own title.

const formSchema = z.object({
  guestDetails: z.object({
    name: z.string(),
    age: z.coerce.number(),
  }),
});

Field configuration

Use the fieldConfig function to customize how a field is rendered. Import it from @acp-autoform/zod.

Depending on the Zod version:

  • Use .check(fieldConfig(...)) for Zod v4 and Zod Mini
  • Use .superRefine(fieldConfig(...)) for Zod v3

Zod v4

import { fieldConfig } from "@acp-autoform/zod";
import * as z from "zod";

const formSchema = z.object({
  username: z
    .string({
      error: "Username is required.",
    })
    .min(2, {
      message: "Username must be at least 2 characters.",
    })
    .default("Default username")
    .check(
      fieldConfig({
        description: "You cannot change this later.",
      }),
    ),
});

Zod Mini

import { fieldConfig } from "@acp-autoform/zod";
import { z } from "zod/mini";

const formSchema = z.object({
  username: z._default(
    z
      .string({
        error: "Username is required.",
      })
      .check(
        z.minLength(2, {
          message: "Username must be at least 2 characters.",
        }),
        fieldConfig({
          description: "You cannot change this later.",
        }),
      ),
    "Default username",
  ),
});

Zod v3

import { fieldConfig } from "@acp-autoform/zod";
import * as z from "zod";

const formSchema = z.object({
  username: z.string().superRefine(
    fieldConfig({
      label: "Username",
      description: "Choose a unique username.",
      inputProps: {
        placeholder: "Enter your username",
      },
    }),
  ),
});

TypeScript Support

Provide external types for full TypeScript support.

import { FieldTypes } from "@acp-autoform/mui";
import { fieldConfig } from "@acp-autoform/zod";

z.string().check(
  fieldConfig<React.ReactNode, FieldTypes, any, { isImportant?: boolean }>({
    inputProps: {
      type: "password",
    },
    customData: {
      isImportant: true,
    },
  }),
);

See the Customization page for all available fieldConfig options.

On this page