---
title: Formisch
description: Build forms in React using Formisch and Valibot.
links:
doc: https://formisch.dev
---
import { InfoIcon } from "lucide-react"
This guide covers building forms with [Formisch](https://formisch.dev), the lightweight, schema-first, and fully type-safe form library for React. We'll create forms with the `` component, validate them with Valibot schemas, handle errors, and ensure accessibility.
## Demo
We'll build the following form. It has a simple text input and a textarea. On submit, we'll validate the form data and display any errors.
}>
**Note:** For the purpose of this demo, we have intentionally disabled browser
validation to show how schema validation and form errors work in Formisch. It
is recommended to add basic browser validation in your production code.
## Approach
This form leverages Formisch for headless, schema-first form handling. We'll build our form using the `` component, which gives you **complete flexibility over the markup and styling**.
- Uses Formisch's `useForm` hook for form state management.
- `
` component to wrap the native `
```
}>
**Note:** Formisch ships its own `Field` component. To avoid a name clash with
the shadcn `Field`, the examples below import the Formisch one as
`FormischField` and keep the shadcn `Field` under its original name. In your
own code you can alias either side — just be consistent.
## Form
### Create a form schema
We'll start by defining the shape of our form using a Valibot schema. Formisch infers all input and output types directly from this schema.
```tsx showLineNumbers title="form.tsx"
import * as v from "valibot"
const FormSchema = v.object({
title: v.pipe(
v.string(),
v.minLength(5, "Bug title must be at least 5 characters."),
v.maxLength(32, "Bug title must be at most 32 characters.")
),
description: v.pipe(
v.string(),
v.minLength(20, "Description must be at least 20 characters."),
v.maxLength(100, "Description must be at most 100 characters.")
),
})
```
### Set up the form
Next, we'll use the `useForm` hook from Formisch to create our form instance. The schema is passed directly to `useForm` — there is no resolver step.
```tsx showLineNumbers title="form.tsx" {1-2,21-25}
import { Form, Field as FormischField, useForm } from "@formisch/react"
import type { SubmitHandler } from "@formisch/react"
import * as v from "valibot"
const FormSchema = v.object({
title: v.pipe(
v.string(),
v.minLength(5, "Bug title must be at least 5 characters."),
v.maxLength(32, "Bug title must be at most 32 characters.")
),
description: v.pipe(
v.string(),
v.minLength(20, "Description must be at least 20 characters."),
v.maxLength(100, "Description must be at most 100 characters.")
),
})
export function BugReportForm() {
const form = useForm({
schema: FormSchema,
initialInput: {
title: "",
description: "",
},
})
const handleSubmit: SubmitHandler = (output) => {
// Do something with the validated form values.
console.log(output)
}
return (
)
}
```
The `` component wraps a native `