Create your form

Formisch consists of hooks, components and methods. To create a form you use the useForm hook.

Form hook

The useForm hook initializes and returns the store of your form. The store contains the state of the form and can be used with other Formisch hooks, components and methods to build your form.

import { Field, Form, useForm } from '@formisch/react';
import * as v from 'valibot';

const LoginSchema = v.object({
  email: v.pipe(v.string(), v.email()),
  password: v.pipe(v.string(), v.minLength(8)),
});

export default function App() {
  const loginForm = useForm({
    schema: LoginSchema,
  });

  return <Form of={loginForm}>{/* Form fields will go here */}</Form>;
}

Configuration options

The useForm hook accepts a configuration object with the following options:

  • schema: Your Valibot schema that defines the form
  • initialInput: Initial values for your form fields (optional)
  • emptyInput: The empty value each field type starts at when a required field has no initial input (optional, defaults to { string: '' })
  • validate: When validation first occurs (optional, defaults to 'submit')
  • revalidate: When revalidation occurs after initial validation (optional, defaults to 'input')
const loginForm = useForm({
  schema: LoginSchema,
  initialInput: {
    email: 'user@example.com',
  },
  validate: 'initial',
  revalidate: 'input',
});

Formisch tracks two inputs for every field: the initial input (baseline for dirty tracking) and the current input (what the user is editing). In many apps, the initial input represents the server state while the current input represents the client state.

isDirty becomes true when a field's current input differs from its initial input. Use setInput to update the current input (client state), and use reset to update the initial input (baseline) when your server data changes or is refreshed.

Empty input

By default, a required string field starts as an empty string ('') instead of undefined, matching an empty text input. This way an empty field shows the validation message you defined for it (for example from v.nonEmpty()) without you setting an initialInput for every field.

You can configure the empty value per field type, or opt out by setting a type to undefined:

const loginForm = useForm({
  schema: LoginSchema,
  emptyInput: {
    string: '', // the default
    number: 0, // required numbers start at 0 instead of undefined
    boolean: false, // checkboxes start unchecked
  },
});

Optional and nullable fields are never affected and keep starting as undefined, since they accept it. The supported types are string, number, boolean and date, and the default is { string: '' }.

Multiple forms

When a page contains multiple forms, you can create separate form stores for each one:

import { Form, useForm } from '@formisch/react';
import * as v from 'valibot';

const LoginSchema = v.object({
  email: v.pipe(v.string(), v.email()),
  password: v.pipe(v.string(), v.minLength(8)),
});

const RegisterSchema = v.object({
  username: v.pipe(v.string(), v.minLength(3)),
  email: v.pipe(v.string(), v.email()),
  password: v.pipe(v.string(), v.minLength(8)),
});

export default function App() {
  const loginForm = useForm({ schema: LoginSchema });
  const registerForm = useForm({ schema: RegisterSchema });

  return (
    <>
      <Form of={loginForm}>{/* … */}</Form>
      <Form of={registerForm}>{/* … */}</Form>
    </>
  );
}

If you need a multi-step form (wizard), see the multi-step form discussion for patterns and approaches.

Next steps

Now that you know how to create a form, continue to the add form fields guide to learn how to connect your input elements to the form using the Field component.

Contributors

Thanks to all the contributors who helped make this page better!

  • GitHub profile picture of @fabian-hiller

Partners

Thanks to our partners who support the project ideally and financially.

Sponsors

Thanks to our GitHub sponsors who support the project financially.

  • GitHub profile picture of @vasilii-kovalev
  • GitHub profile picture of @UpwayShop
  • GitHub profile picture of @ruiaraujo012
  • GitHub profile picture of @hyunbinseo
  • GitHub profile picture of @nickytonline
  • GitHub profile picture of @kibertoad
  • GitHub profile picture of @caegdeveloper
  • GitHub profile picture of @Thanaen
  • GitHub profile picture of @bmoyroud
  • GitHub profile picture of @ysknsid25
  • GitHub profile picture of @dslatkin