Define your form

If you use TypeScript, creating a form starts with its type definition. If you only use JavaScript, you can skip this page.

Type definition

Modular Forms was designed with type safety in mind. Our API design reduces errors, enables autocompletion and points you to the respective location in case of problems. What makes Modular Forms special is that it can handle not only strings and files, but also booleans, numbers, dates, objects and arrays.

Example type

Below you will find the type definition of our special form from the playground that demonstrates different data types:

type SpecialForm = {
  number: number;
  range: number;
  checkbox: {
    array: string[];
    boolean: boolean;
  select: {
    array: string[];
    string?: string;
  file: {
    list: NoSerialize<Blob>[];
    item?: NoSerialize<Blob>;

It is recommended to use type instead of interface as this currently requires an explicit index signature in TypeScript. More about this here.

Data types

When selecting the respective types of your fields, keep in mind that an HTML input element can usually represent different types of data. For example, <input type="date" /> can return a string, date or number. With Modular Forms you can freely choose the data type that best suits your needs. More details and special cases can be found in the special inputs, nested fields and field arrays guide.

Infer from schema

If you prefer to infer your type definition from a schema, you can optionally use Valibot or Zod for that. Below is an example with Valibot:

import { type NoSerialize } from '';
import * as v from 'valibot';

const isBlob = (input: unknown) => input instanceof Blob;

const SpecialSchema = v.object({
  number: v.number(),
  range: v.number(),
  checkbox: v.object({
    array: v.array(v.string()),
    boolean: v.boolean(),
  select: v.object({
    array: v.array(v.string()),
    string: optional(v.string()),
  file: v.object({
    list: v.array(v.custom<NoSerialize<Blob>>(isBlob)),
    item: optional(v.custom<NoSerialize<Blob>>(isBlob)),

type SpecialForm = v.InferInput<typeof SpecialSchema>;

Type rules

Your type definition should reflect exactly the data you expect when submitting the form. For example, if the value of a field is optional and will only be submitted in certain cases, your type definition should reflect this information. To ensure that your form can only be submitted if it matches your type definition, you should validate the values of your fields in advance. More information about validation will follow later.

To keep it simple, in the following guides we will focus on creating a login form with the following type definition:

type LoginForm = {
  email: string;
  password: string;