Formisch
@formisch/preact + valibot
Installation
Add Formisch and Valibot to the docs app. Kamod UI components stay responsible for layout and interaction, while Formisch owns the schema-backed form state.
Usage
Create a Valibot object schema, pass it to useForm, wrap controls in FormischForm, and use FormischField render props to connect Kamod UI controls.
Demo
This bug-report form validates on submit and revalidates as you edit after the first submit.
Approach
The integration is headless: Formisch supplies typed input, errors, and methods; Kamod UI supplies accessible Field, Input, Select, Checkbox, RadioGroup, Switch, Button, and Card primitives.
Form Methods
Import only the methods you need. The methods API can inspect, validate, submit, reset, focus, and update deeply nested fields or field arrays.
import { Field as FormischField, FieldArray, Form as FormischForm, insert, remove, reset, useForm } from "@formisch/preact";
import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Checkbox, Field, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSet, Input, RadioGroup, RadioGroupItem, Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, Switch, Textarea } from "@kamod-ch/ui";
import * as v from "valibot";
import { focus, getErrors, getInput, move, replace, setErrors, setInput, submit, swap, validate } from "@formisch/preact";
const form = useForm({ schema, initialInput, validate: "submit", revalidate: "input" });
getInput(form, { path: ["email"] });
setInput(form, { path: ["email"], input: "hello@kamod.ch" });
getErrors(form, { path: ["email"] });
setErrors(form, { path: ["email"], errors: ["Use a work email."] });
await validate(form, { shouldFocus: true });
submit(form);
focus(form, { path: ["email"] });
insert(form, { path: ["emails"], at: 0, initialInput: "" });
remove(form, { path: ["emails"], at: 0 });
move(form, { path: ["emails"], from: 0, to: 1 });
swap(form, { path: ["emails"], at: 0, and: 1 });
replace(form, { path: ["emails"], at: 0, initialInput: "team@kamod.ch" });
reset(form);API Reference
The most important integration APIs are useForm, Form, Field, FieldArray, reset, insert, remove, and the optional methods shown below.
Anatomy
A typical form has a Valibot schema, a useForm call, a FormischForm, one or more FormischField blocks, Kamod Field wrappers, visible errors, and action buttons.
Schema and Form Setup
Valibot is the single source of truth. Input and output types are inferred from the schema, so submit handlers receive validated data.
Validation
Use Valibot pipes for length, email, picklist, array, and boolean constraints. Formisch returns field-level error strings that map directly to FieldError.
Validation Modes
Choose when the first validation happens with validate, and when later checks happen with revalidate.
const submitOnly = useForm({ schema, validate: "submit" });
const blurFirst = useForm({ schema, validate: "blur", revalidate: "input" });
const live = useForm({ schema, validate: "input" });
const validateImmediately = useForm({ schema, validate: "initial" });
const revalidateOnBlur = useForm({ schema, validate: "submit", revalidate: "blur" });
const revalidateOnSubmit = useForm({ schema, validate: "blur", revalidate: "submit" });Displaying Errors
Set invalid state on Field, aria-invalid on the actual control, and render FieldError only when Formisch has messages.
Input
Native inputs can spread field.props and normalize undefined to an empty string for controlled rendering.
Textarea
Textareas use the same binding model as inputs and can display length counters next to validation feedback.
Select
Composite controls use their value callback. Read field.input, pass it to the control, and call field.onChange with the next value.
Checkbox
For checkbox groups, keep arrays immutable: add with a new array and remove with filter.
Radio Group
RadioGroup maps one selected string to a Valibot picklist.
Switch
Switch maps a controlled boolean to Formisch state.
Complex Forms
Larger forms compose the same primitives for plan, billing, add-ons, and email preferences.
Resetting the Form
Call reset(form) to restore initial inputs and clear validation state. Reset buttons are type=button so they do not submit.
Array Fields
FieldArray exposes stable item IDs. Use insert and remove to manage dynamic rows and let Valibot enforce min and max lengths.
Accessibility
Every preview uses stable IDs, explicit labels, fieldsets for groups, aria-invalid, alert-based errors, and descriptive remove buttons.
Sources
Related references for the ideas and APIs used on this page.