Formisch

@formisch/preact + valibot

Bug report
Share a concise title and a reproducible description.

5–32 characters.

0/100 characters. Minimum 20.

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.

Bug report
Share a concise title and a reproducible description.

5–32 characters.

0/100 characters. Minimum 20.

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.

Profile settings
A native input bound to a Formisch field.

Only lowercase letters, numbers, underscores and dashes. 3–20 characters.

Textarea

Textareas use the same binding model as inputs and can display length counters next to validation feedback.

Personalization
Textarea fields use the same Formisch props as native inputs.

0/160 characters. Minimum 24.

Select

Composite controls use their value callback. Read field.input, pass it to the control, and call field.onChange with the next value.

Language preferences
Component controls call field.onInput with their selected value.

Used for examples and localized content.

Checkbox

For checkbox groups, keep arrays immutable: add with a new array and remove with filter.

Notifications
Checkbox groups update array values without mutation.
Notification channels

Select at least one category.

Radio Group

RadioGroup maps one selected string to a Valibot picklist.

Subscription plan
RadioGroup keeps the selected value in Formisch state.
Plan

Switch

Switch maps a controlled boolean to Formisch state.

Security settings
Switch controls pass booleans to Formisch.

Require a second factor for sign-in.

Complex Forms

Larger forms compose the same primitives for plan, billing, add-ons, and email preferences.

Complex subscription form
A larger form combines radio groups, select, checkbox arrays, and switches.
Subscription Plan
Add-ons

Resetting the Form

Call reset(form) to restore initial inputs and clear validation state. Reset buttons are type=button so they do not submit.

Bug report
Share a concise title and a reproducible description.

5–32 characters.

0/100 characters. Minimum 20.

Array Fields

FieldArray exposes stable item IDs. Use insert and remove to manage dynamic rows and let Valibot enforce min and max lengths.

Contact emails
FieldArray supplies stable item keys while insert and remove update the array.
Contact Emails

Add up to five addresses.

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.