# Formisch
> The lightweight, schema-first and fully type-safe form library for Angular, HonoX, Preact, Qwik, React, React Native, Solid, Svelte and Vue.
## Get started (guides)
### Introduction
Formisch is a schema-based, headless form library for HonoX. It manages form state and validation. It is type-safe, fast by default and its bundle size is small due to its modular design. Try it out in our [playground](/playground/login/)!
#### Highlights
- Small bundle size starting at 2.5 kB
- Schema-based validation with Valibot
- Type safety with autocompletion in editor
- Open source and fully tested with 100% coverage
- It's fast – re-renders only if necessary
- Minimal, readable and well thought out API
- Supports all native HTML form fields
#### Example
Every form starts with the [`useForm`](/honox/api/useForm.md) hook. It initializes your form's store based on the provided Valibot schema and infers its types. Next, wrap your form in the [`
`](/honox/api/Form.md) component. It's a thin layer around the native `` element that handles form validation and submission. Then, you can access the state of a field with the [`useField`](/honox/api/useField.md) hook or the [``](/honox/api/Field.md) component to connect your inputs. Since forms are interactive, place them in a HonoX island so that they are rendered on the server and hydrated on the client.
```tsx
// app/islands/LoginForm.tsx
import { Field, Form, useForm } from '@formisch/honox';
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 LoginForm() {
const loginForm = useForm({
schema: LoginSchema,
});
return (
);
}
```
In addition, Formisch offers several functions (we call them "methods") that can be used to read and manipulate the form state. These include [`focus`](/methods/api/focus.md), [`getDeepErrorEntries`](/methods/api/getDeepErrorEntries.md), [`getDeepErrors`](/methods/api/getDeepErrors.md), [`getDirtyInput`](/methods/api/getDirtyInput.md), [`getDirtyPaths`](/methods/api/getDirtyPaths.md), [`getErrors`](/methods/api/getErrors.md), [`getInput`](/methods/api/getInput.md), [`handleSubmit`](/methods/api/handleSubmit.md), [`insert`](/methods/api/insert.md), [`isDirty`](/methods/api/isDirty.md), [`isEdited`](/methods/api/isEdited.md), [`isTouched`](/methods/api/isTouched.md), [`isValid`](/methods/api/isValid.md), [`move`](/methods/api/move.md), [`pickDirty`](/methods/api/pickDirty.md), [`remove`](/methods/api/remove.md), [`replace`](/methods/api/replace.md), [`reset`](/methods/api/reset.md), [`setErrors`](/methods/api/setErrors.md), [`setInput`](/methods/api/setInput.md), [`submit`](/methods/api/submit.md), [`swap`](/methods/api/swap.md) and [`validate`](/methods/api/validate.md). These methods allow you to control the form programmatically.
#### Comparison
What makes Formisch unique is its framework-agnostic core, which is fully native to the framework you are using. It works by inserting framework-specific reactivity blocks when the core package is built, giving you native performance for any UI update. A modular methods API keeps bundles starting at just ~2.5 kB by only including the methods you import, and end-to-end type safety covers deeply nested paths and field arrays with TypeScript inference that stays fast even as forms grow.
For a side-by-side look at how Formisch compares to React Hook Form and TanStack Form, see the [comparison guide](/honox/guides/comparison.md).
#### Vision
My vision for Formisch is to create a framework-agnostic platform similar to [Vite](https://vite.dev/), but for forms — a shared core that lets the same mental model and codebase work natively across every modern UI framework.
#### Feedback
Find a bug or have an idea how to improve the library? Please fill out an [issue](https://github.com/open-circle/formisch/issues/new). Together we can make forms even better!
#### License
This project is available free of charge and licensed under the [MIT license](https://github.com/open-circle/formisch/blob/main/LICENSE.md).
### Installation
Below you will learn how to add Formisch to your project.
#### TypeScript
If you are using TypeScript, we recommend that you enable strict mode in your `tsconfig.json` so that all types are calculated correctly.
> The minimum required TypeScript version is v5.0.2.
```jsonc
{
"compilerOptions": {
"strict": true,
// ...
},
}
```
#### Install Valibot
Formisch uses [Valibot](https://valibot.dev/) for schema-based validation. You need to install it first because it is a peer dependency.
```bash
npm install valibot # npm
yarn add valibot # yarn
pnpm add valibot # pnpm
bun add valibot # bun
deno add npm:valibot # deno
```
#### Install Formisch
You can add Formisch to your project with a single command using your favorite package manager.
```bash
npm install @formisch/honox # npm
yarn add @formisch/honox # yarn
pnpm add @formisch/honox # pnpm
bun add @formisch/honox # bun
deno add npm:@formisch/honox # deno
```
Formisch for HonoX also requires `hono` v4.5 or later as a peer dependency, which your HonoX app already includes. Then you can import it into any JavaScript or TypeScript file.
```ts
import { … } from '@formisch/honox';
```
#### For AI Agents
We provide agent skills that teach AI agents the correct patterns for working with Valibot and Formisch. You can install them by running the following command in your terminal:
```bash
npx skills add open-circle/agent-skills --skill formisch valibot
```
You can learn more about the Valibot and Formisch agent skill [here](https://github.com/open-circle/agent-skills).
### Coding agents
Formisch is built to be consumed by AI coding agents. This page describes the agent skill, MCP server and Markdown documentation we provide so that your AI tools generate correct HonoX code with Formisch.
#### Agent skill
Formisch defines form values and validation with [Valibot](https://valibot.dev/) schemas, so an agent needs two skills: our [Formisch SKILL.md](https://github.com/open-circle/agent-skills/blob/main/skills/formisch/SKILL.md) for building forms and managing form state, and the [Valibot SKILL.md](https://github.com/open-circle/agent-skills/blob/main/skills/valibot/SKILL.md) for writing the schemas. Install both with the [Agent Skills CLI](https://agentskills.io/):
```bash
npx skills add open-circle/agent-skills --skill formisch valibot
```
The Formisch skill is also published at [`/.well-known/agent-skills/formisch/SKILL.md`](/.well-known/agent-skills/formisch/SKILL.md) and listed in our [discovery index](/.well-known/agent-skills/index.json). For Valibot's MCP server and llms.txt files, see [Valibot's coding agents guide](https://valibot.dev/guides/coding-agents/).
#### MCP server
We host a [Model Context Protocol](https://modelcontextprotocol.io/) server at `https://formisch.dev/mcp` that gives coding agents first-class tools to search and read the documentation instead of crawling pages. It is free and requires no authentication.
The server provides three tools:
- `search_docs` searches the documentation and returns the most relevant pages
- `get_doc` reads a documentation page or blog post as Markdown
- `list_docs` lists all pages grouped by framework and area
Add it to Claude Code with:
```bash
claude mcp add --transport http formisch https://formisch.dev/mcp
```
Or add it to the MCP configuration of your tool:
```jsonc
{
"mcpServers": {
"formisch": {
"url": "https://formisch.dev/mcp",
},
},
}
```
The server metadata is available at [`/.well-known/mcp/server-card.json`](/.well-known/mcp/server-card.json).
#### LLMs.txt
We provide several [LLMs.txt](https://llmstxt.org/) files. Use the one that works best with your AI tool.
- [`llms.txt`](/llms.txt) contains a table of contents with links to all Markdown files
- [`llms-honox.txt`](/llms-honox.txt) contains a table of contents with links to HonoX-related files
- [`llms-honox-full.txt`](/llms-honox-full.txt) contains the Markdown content of the entire HonoX docs
- [`llms-honox-guides.txt`](/llms-honox-guides.txt) contains the Markdown content of the HonoX guides
- [`llms-honox-api.txt`](/llms-honox-api.txt) contains the Markdown content of the HonoX API reference
- [`llms-blog.txt`](/llms-blog.txt) contains the Markdown content of all blog posts
#### Markdown for agents
Every documentation page is available as Markdown. Append `.md` to the URL of a page (e.g. `/honox/guides/installation/` becomes [`/honox/guides/installation.md`](/honox/guides/installation.md)) or request the page with an `Accept: text/markdown` header to receive its Markdown version.
#### Custom building blocks
Coding agents can use `@formisch/honox/internals` to build custom methods and framework bindings with the same core helpers as Formisch. See the [internals guide](/honox/guides/internals.md) for a typed example and guidance on reactivity and lifecycle behavior.
### Comparison
Formisch runs natively on HonoX and hono/jsx. The two most common form libraries in the JSX ecosystem, [React Hook Form](https://react-hook-form.com) and [TanStack Form](https://tanstack.com/form/latest), are built for React and do not provide bindings for hono/jsx. If you know them from React, this page is meant as a quick reference for picking the right tool. For a deeper explanation of the architectural differences and the reasoning behind each row of the table below, see our [long-form comparison article](/blog/react-form-library-comparison.md) — it was written for React, but the architecture it describes applies equally to HonoX.
#### At a glance
| | **Formisch** | React Hook Form | TanStack Form |
| ---------------------- | --------------------------------------------------------------------- | --------------------------------------------- | --------------------------------------- |
| Type source | Inferred from schema | Generic you declare | Inferred from `defaultValues` |
| Validation location | Defined in schema | Per-field, resolver, or form-level `validate` | Per-validator config |
| Validation timing | Form-wide `validate` / `revalidate` | Form-wide `mode` option | Per-validator trigger |
| Async validation | Built-in via schema | Manual loading state | Built-in `isValidating` |
| Re-render scope | Automatic per signal | Manual via `watch` / `useFormState` | Automatic per subscription |
| Schema libraries | Valibot | Any via resolvers | Standard Schema |
| Bundle size (min+gzip) | From ~2.5 kB | ~12 kB | ~15 kB |
| Framework support | Angular, React, React Native, Preact, Solid, Svelte, Vue, Qwik, HonoX | React | React, Vue, Solid, Svelte, Lit, Angular |
The table is intentionally short. It only covers the dimensions that most often drive a library choice in practice. Other differences such as devtools, ecosystem maturity, and community size are real but tend to matter less than how each library handles types, validation, and re-renders.
#### Why Formisch?
Three reasons to pick Formisch over the alternatives above:
**One schema, no second source of truth.** A single Valibot schema is everything the form needs: the runtime validator, the source of types, and the description of the form's structure — all at once. There is no separate TypeScript generic to declare, no `defaultValues` object to keep aligned with the schema, no resolver to configure. When the schema changes, every part of the form follows — at compile time and at runtime.
**The smallest bundle, by a wide margin.** Formisch starts at ~2.5 kB and grows only as you import additional methods like `focus`, `getInput`, and `reset`. That is several times smaller than the alternatives in the table above — and it stays that way because the core is intentionally small and the library is fully tree-shakeable, so methods you don't import don't end up in your bundle.
**Type safety that stays fast.** Types flow from the schema through every API, including deeply nested paths and field arrays. The inference is structured to keep TypeScript editor performance from degrading as schemas grow — which matters in large codebases where heavily-generic form libraries become a friction point.
#### Which library should you use?
**React Hook Form** is a mature, well-documented choice with a large community and years of production usage. It requires React, so in a HonoX app it is only an option if you render your islands with React instead of hono/jsx. For simple to moderately complex forms, it remains a reasonable default. The tradeoffs only become visible as forms grow: types and schema drift out of sync, validation rules spread across components, and re-render scope becomes your responsibility.
**TanStack Form** is a good fit when you need fine-grained control over validation timing and built-in async validation handling without building that infrastructure yourself. Like React Hook Form, it has no hono/jsx adapter, so in a HonoX app you would have to render your islands with one of its supported frameworks. It is also the natural choice if your team is already invested in the TanStack ecosystem and values a consistent mental model across data fetching, routing, and forms.
**Formisch** makes the most sense for new projects in TypeScript-heavy codebases, especially when you expect forms to grow in complexity. The schema-first design means there is a single source of truth for types, runtime validation, and form structure, so there is less to keep aligned over time. The main consideration is that Formisch currently supports only [Valibot](https://valibot.dev) as the schema library.
#### Migrating to Formisch
Migrating to Formisch is a genuine rewrite of the form layer, not a drop-in replacement. The mental model is different: instead of starting from a component and attaching form behavior to it, you start from a schema and build the component around it.
#### Next steps
If you have decided that Formisch is a good fit, install it via the [installation](/honox/guides/installation.md) guide and start building by [defining your form](/honox/guides/define-your-form.md).
## Main concepts (guides)
### Define your form
Creating a form in Formisch starts with defining a Valibot schema. The schema serves as the blueprint for your form, outlining the structure, data types, and validation rules for each field.
#### Schema definition
Formisch is a schema-first form library built on top of [Valibot](https://valibot.dev/). When you create a form with [`useForm`](/honox/api/useForm.md), TypeScript types are automatically inferred from your schema, giving you full autocompletion and type safety throughout your form without needing to write any manual type definitions.
##### Example schema
The following schema defines a form with two required string fields. The `email` field must be a valid email format, and the `password` field must be at least 8 characters long. Each validation includes custom error messages that will be displayed when validation fails.
> For more complex schema examples, check out the schemas of our [playground](/playground/login/).
```ts
import * as v from 'valibot';
const LoginSchema = v.object({
email: v.pipe(
v.string(),
v.nonEmpty('Please enter your email.'),
v.email('The email address is badly formatted.')
),
password: v.pipe(
v.string(),
v.nonEmpty('Please enter your password.'),
v.minLength(8, 'Your password must have 8 characters or more.')
),
});
```
##### Field order
The order in which you define fields in your schema matters. When a form is submitted with invalid values, Formisch focuses the first field with an error. Since this follows your schema, define your fields in the same order they appear in your form, so the focus moves to the first invalid field instead of one further down the page.
#### Schema validation
Your schema 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 schema should reflect this information by using `v.optional(…)`.
```ts
import * as v from 'valibot';
const ProfileSchema = v.object({
name: v.pipe(v.string(), v.nonEmpty()),
bio: v.optional(v.string()), // <- Optional field
});
```
Formisch validates your form values against the schema before submission, ensuring that your form can only be submitted if it matches your schema definition.
#### Next steps
Now that you understand how to define your form schema, continue to the [create your form](/honox/guides/create-your-form.md) guide to learn how to initialize your form with [`useForm`](/honox/api/useForm.md).
### Create your form
Formisch consists of hooks, components and methods. To create a form you use the [`useForm`](/honox/api/useForm.md) hook.
#### Form hook
The [`useForm`](/honox/api/useForm.md) 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.
```tsx
// app/islands/LoginForm.tsx
import { Field, Form, useForm } from '@formisch/honox';
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 LoginForm() {
const loginForm = useForm({
schema: LoginSchema,
});
return (
);
}
```
##### Configuration options
The [`useForm`](/honox/api/useForm.md) 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'`)
```tsx
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`](/methods/api/setInput.md) to update the current input (client state), and use [`reset`](/methods/api/reset.md) 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`:
```tsx
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: '' }`.
#### Render in an island
Forms are interactive, so the component that creates the form must be a HonoX island. Place it in the `app/islands` directory and export it as default, as in the example above. You can then render it from any route like a regular component:
```tsx
// app/routes/login/index.tsx
import { createRoute } from 'honox/factory';
import LoginForm from '../../islands/LoginForm';
export default createRoute((c) => c.render());
```
HonoX renders the island on the server and hydrates it on the client. During server-side rendering, effects do not run and form state is not tracked, so the form simply renders its initial state and becomes interactive after hydration. Props passed to an island are serialized as JSON, so pass plain data, such as the initial input of your form, rather than functions.
Until the island is hydrated, the browser submits the form natively. By default, this sends a GET request with all field values in the URL. For forms with sensitive data such as passwords, set `method="post"` on the `` component to keep the values out of the URL, and make sure that the route handles the POST request, for example by exporting a `POST` handler created with `createRoute`. After hydration, Formisch handles the submission and the `method` has no effect.
#### Multiple forms
When a page contains multiple forms, you can create separate form stores for each one:
```tsx
import { Form, useForm } from '@formisch/honox';
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 (
<>
>
);
}
```
If you need a multi-step form (wizard), see the [multi-step form discussion](https://github.com/open-circle/formisch/discussions/108) for patterns and approaches.
#### Next steps
Now that you know how to create a form, continue to the [add form fields](/honox/guides/add-form-fields.md) guide to learn how to connect your input elements to the form using the [`Field`](/honox/api/Field.md) component.
### Add form fields
To add a field to your form, you can use the [`Field`](/honox/api/Field.md) component or the [`useField`](/honox/api/useField.md) hook. Both are headless and provide access to field state for building your form UI.
#### Field component
The [`Field`](/honox/api/Field.md) component has two mandatory properties: `of` which accepts the form store, and `path` which specifies which field to connect. If you use TypeScript, you get full autocompletion for the path based on your schema.
##### Children prop
As a child, you pass a function to the [`Field`](/honox/api/Field.md) component that returns JSX. The function receives the field store as its parameter, which includes the current value, error messages, and props to spread onto your input element.
```tsx
import { Field, Form, useForm } from '@formisch/honox';
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 (
);
}
```
> **Important:** The event handlers in `field.props` read the value from the DOM as a string. Fields with a non-string schema like `v.number()` or `v.date()` therefore need to be controlled to store the value with the correct type. See the [controlled fields](/honox/guides/controlled-fields.md) guide to learn more.
> **Important:** If you plan to set initial values with `initialInput` or programmatically control field values using methods like [`setInput`](/methods/api/setInput.md) or [`reset`](/methods/api/reset.md), you must make your fields controlled by setting the appropriate attributes (like `value`, `checked`, or `selected`). See the [controlled fields](/honox/guides/controlled-fields.md) guide to learn more.
##### Headless design
The [`Field`](/honox/api/Field.md) component does not render its own UI elements. It is headless and provides only the data layer of the field. This allows you to freely define your user interface. You can use HTML elements, custom components or an external UI library.
##### Path array
The `path` property accepts an array of strings and numbers that represents the path to the field in your schema. For top-level fields, it's simply the field name wrapped in an array:
```tsx
{(field) => }
```
For nested fields, the path reflects the structure of your schema:
```tsx
// For a schema like: v.object({ user: v.object({ email: v.string() }) })
{(field) => }
```
##### Type safety
The API design of the [`Field`](/honox/api/Field.md) component results in a fully type-safe form. For example, if you change your schema, TypeScript will immediately alert you if the path is invalid. The field state is also fully typed based on your schema, giving you autocompletion for properties like `field.input`.
#### useField hook
For very complex forms where you create individual components for each form field, Formisch provides the [`useField`](/honox/api/useField.md) hook. It allows you to access the field state directly within your component logic.
```tsx
import { useField } from '@formisch/honox';
import type { FormStore } from '@formisch/honox';
import * as v from 'valibot';
type EmailInputProps = {
form: FormStore>;
};
function EmailInput(props: EmailInputProps) {
const field = useField(props.form, { path: ['email'] });
return (
{field.errors &&
{field.errors[0]}
}
);
}
```
##### When to use which
- **Use [`Field`](/honox/api/Field.md) component**: When defining multiple fields in the same component. It ensures you don't accidentally access the wrong field store.
- **Use [`useField`](/honox/api/useField.md) hook**: When creating field components for single fields. It allows you to access field state in your component logic.
The [`Field`](/honox/api/Field.md) component is essentially a thin wrapper around [`useField`](/honox/api/useField.md) that allows you to access the field state within JSX code.
#### Field store
The field store provides access to the following properties. They are plain values, but because Formisch wires into the rendering of hono/jsx, your components re-render when they change.
- `path`: The path to the field within the form.
- `props`: JSX props to spread onto your input element (includes event handlers, ref callback, name attribute, and `autofocus` to automatically focus fields with errors).
- `input`: The current input value of the field.
- `errors`: The current array of error messages if validation fails.
- `isTouched`: Whether the field has been touched.
- `isEdited`: Whether the field's value has been changed.
- `isDirty`: Whether the current input differs from the initial input.
- `isValid`: Whether the field passes all validation rules.
- `onChange`: Sets the field input value programmatically. Use this when the field cannot be connected to a native HTML element (e.g., complex custom inputs or component libraries that don't expose the underlying element).
#### Next steps
Now that you know how to add fields to your form, continue to the [input components](/honox/guides/input-components.md) guide to learn about creating reusable input components for your forms.
### Input components
To make your code more readable, we recommend that you develop your own input components if you are not using a prebuilt UI library. There you can encapsulate logic to display error messages, for example.
> If you're already a bit more experienced, you can use the input components we developed for our [playground](/playground/login/) as a starting point. You can find the code in our GitHub repository [here](https://github.com/open-circle/formisch/tree/main/playgrounds/honox/app/components).
#### Why input components?
Currently, your fields might look something like this:
```tsx
{(field) => (
{field.errors &&
{field.errors[0]}
}
)}
```
If CSS and a few more functionalities are added here, the code quickly becomes confusing. In addition, you have to rewrite the same code for almost every form field.
Our goal is to develop a `TextInput` component so that the code ends up looking like this:
```tsx
{(field) => (
)}
```
#### Create an input component
In the first step, you create a new file for the `TextInput` component and, if you use TypeScript, define its properties.
```tsx
import type { FieldElementProps } from '@formisch/honox';
interface TextInputProps extends FieldElementProps {
type: 'text' | 'email' | 'tel' | 'password' | 'url' | 'date';
label?: string;
placeholder?: string;
input: string | undefined;
errors: [string, ...string[]] | null;
required?: boolean;
}
```
##### Component function
In the next step, add the component function to the file. We can destructure props directly and spread the remaining props.
```tsx
import type { FieldElementProps } from '@formisch/honox';
interface TextInputProps extends FieldElementProps {
/* ... */
}
export function TextInput({ label, input, errors, ...props }: TextInputProps) {
// Component implementation
}
```
##### JSX code
After that, you can add the JSX code to the return statement.
```tsx
import type { FieldElementProps } from '@formisch/honox';
interface TextInputProps extends FieldElementProps {
/* ... */
}
export function TextInput({ label, input, errors, ...props }: TextInputProps) {
const { name, required } = props;
return (
{label && (
)}
{errors &&
{errors[0]}
}
);
}
```
##### Next steps
You can now build on this code and add CSS, for example. You can also follow the procedure to create other components such as `Checkbox`, `Slider`, `Select` and `FileInput`. When building a `Select` component, keep in mind that hono/jsx does not apply the `value` property of a `` element during server-side rendering. Set the `selected` attribute on its `` elements instead, as shown in the [special inputs](/honox/guides/special-inputs.md) guide.
##### Final code
Below is an overview of the entire code of the `TextInput` component.
```tsx
import type { FieldElementProps } from '@formisch/honox';
interface TextInputProps extends FieldElementProps {
type: 'text' | 'email' | 'tel' | 'password' | 'url' | 'date';
label?: string;
placeholder?: string;
input: string | undefined;
errors: [string, ...string[]] | null;
required?: boolean;
}
export function TextInput({ label, input, errors, ...props }: TextInputProps) {
const { name, required } = props;
return (
{label && (
)}
{errors &&
{errors[0]}
}
);
}
```
#### Using component libraries
When using component libraries that don't expose their underlying native HTML elements, you cannot spread `field.props` directly. Instead, use `field.onChange` to update the value programmatically:
```tsx
import { DatePicker } from 'some-component-library';
{(field) => (
field.onChange(newDate)}
/>
)}
;
```
The `field.onChange` method updates the field value and triggers validation. For more details, see the [controlled fields](/honox/guides/controlled-fields.md) guide.
#### Next steps
Now that you know how to create reusable input components, continue to the [handle submission](/honox/guides/handle-submission.md) guide to learn how to process form data when the user submits the form.
### Handle submission
Your first form is almost ready. The remaining step is processing its data when the form is submitted.
#### Submit event
To process the values on submission, you need to pass a function to the `onSubmit` property of the [`Form`](/honox/api/Form.md) component. The first parameter passed to the function contains the validated form values.
```tsx
import { Field, Form, useForm } from '@formisch/honox';
import type { SubmitHandler } from '@formisch/honox';
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,
});
const submitForm: SubmitHandler = (values) => {
// Process the validated form values
console.log(values); // { email: string, password: string }
};
return (
);
}
```
The [`SubmitHandler`](/core/api/SubmitHandler.md) type ensures type safety for your submission handler, automatically inferring the types of validated values from your schema. If you need access to the submit event, use [`SubmitEventHandler`](/core/api/SubmitEventHandler.md) instead.
##### Prevent default
When the form is submitted, `event.preventDefault()` is executed by default to prevent the window from reloading so that the values can be processed directly in the browser and the state of the form is preserved.
##### Loading state
While the form is being submitted, you can use `loginForm.isSubmitting` to display a loading animation and disable the submit button:
```tsx
```
The form store also provides other reactive properties like `isSubmitted`, `isValidating`, `isTouched`, `isDirty`, `isValid`, and `errors` for tracking form state. Note that `errors` only contains validation errors at the root level of the form — to get all errors from all fields, use the [`getDeepErrors`](/methods/api/getDeepErrors.md) method.
##### Async submission
The submit handler can be asynchronous, allowing you to perform API calls or other async operations:
```tsx
const submitForm: SubmitHandler = async (values) => {
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(values),
});
if (response.ok) {
// Handle successful login
console.log('Login successful!');
} else {
// Handle error
console.error('Login failed');
}
} catch (error) {
console.error('Error during submission:', error);
}
};
```
##### Trigger submission
If you want to trigger submission programmatically from outside the form, you can use the [`submit`](/methods/api/submit.md) method. It calls `requestSubmit()` on the underlying form element:
```tsx
import { submit } from '@formisch/honox';
;
```
#### Submit without \
);
}
```
`isEdited` is the right flag for this. `isTouched` would reveal the error as soon as the user tabs through a field without changing it, and `isDirty` would hide the error again if the user clears an invalid value back to its initial state. `isEdited` avoids both: it turns on only after a real change and stays on. Pair it with `validate: 'input'` (or `'change'`) so the error appears as soon as the field becomes invalid, and with `isSubmitted` so that the errors of all fields — including the ones the user never touched — become visible after a submit attempt.
##### Reuse the guard across fields
Writing this guard at every field gets repetitive. The common fix is to move it into a small reusable component, either on its own or folded into the `TextInput` from the [input components](/honox/guides/input-components.md) guide, so the `isEdited || isSubmitted` logic lives in exactly one place:
```tsx
interface FieldErrorProps {
errors: [string, ...string[]] | null;
isEdited: boolean;
isSubmitted: boolean;
}
export function FieldError({ errors, isEdited, isSubmitted }: FieldErrorProps) {
if (!errors || (!isEdited && !isSubmitted)) {
return null;
}
return
{errors[0]}
;
}
```
Each field then stays focused on its input:
```tsx
{(field) => (
)}
```
#### Validating state
When your schema contains asynchronous checks, validation does not resolve instantly. The form's `isValidating` state is `true` while any validation is in flight, which you can use to show a loading indicator or to disable the submit button until validation settles.
```tsx
import { Form, useForm } from '@formisch/honox';
import * as v from 'valibot';
import { isUsernameAvailable } from '~/api';
const SignUpSchema = v.objectAsync({
username: v.pipeAsync(
v.string(),
v.nonEmpty(),
v.checkAsync(isUsernameAvailable, 'Username is already taken.')
),
});
export default function App() {
const signUpForm = useForm({
schema: SignUpSchema,
validate: 'blur',
});
return (
console.log(output)}>
{/* fields */}
);
}
```
`isValidating` and `isSubmitting` overlap but are not the same. `isValidating` is `true` whenever validation runs — triggered by a field event or as part of a submit. `isSubmitting` is `true` for the entire submission, which covers both validating the form and running your submit handler. So during a submit both are `true` while the form validates, and once validation passes only `isSubmitting` stays `true` while your handler runs.
#### Further reading
To process your form's values once they pass validation, see the [handle submission](/honox/guides/handle-submission.md) guide. For working with the fields a user has changed, see the [dirty fields](/honox/guides/dirty-fields.md) guide.
### Special inputs
As listed in our features, the library supports all native HTML form fields. This includes the HTML `` element as well as special cases of the `` element.
> In our [playground](/playground/special/) you can take a look at such fields and test them out.
#### Checkbox
A simple checkbox represents a boolean and is `true` when checked or `false` otherwise.
```tsx
{(field) => (
)}
```
However, you can also use multiple checkboxes to represent an array of strings. For this you simply have to add the `value` attribute to the HTML `` element.
```tsx
<>
{[
{ label: 'Bananas', value: 'bananas' },
{ label: 'Apples', value: 'apples' },
{ label: 'Grapes', value: 'grapes' },
].map(({ label, value }) => (
{(field) => (
)}
))}
>
```
#### Radio
A group of radio buttons, while similar to an array of checkboxes, will only allow you to select one of the options based on which button is checked.
```tsx
{(field) => (
<>
{[
{ label: 'Red', value: 'red' },
{ label: 'Green', value: 'green' },
{ label: 'Blue', value: 'blue' },
].map(({ label, value }) => (
))}
>
)}
```
#### Select
An HTML `` element allows you to select a string from a predefined list of options. Since hono/jsx does not apply the `value` property of a `` element during server-side rendering, you mark the selected option with its `selected` attribute instead.
```tsx
{(field) => (
)}
```
However, if you set the `multiple` attribute, multiple options can be selected making the field represent an array of strings.
```tsx
{(field) => (
// Set "multiple" to "true"
)}
```
#### File
An HTML `` works similarly to an HTML `` element. Without the `multiple` attribute, it represents a single file; with `multiple`, it represents a list of files.
```tsx
{(field) => }
```
With the `multiple` attribute, users can select multiple files:
```tsx
{(field) => }
```
### Controlled fields
By default, all form fields are uncontrolled because that's the default behavior of the browser. For a simple login or contact form this is quite sufficient.
#### Why controlled?
As soon as your forms become more complex, for example you set initial values or change the values of a form field via [`setInput`](/methods/api/setInput.md), it becomes necessary that you control your fields yourself. For example, depending on which HTML form field you use, you may need to set the `value`, `checked` or `selected` attributes. Since HonoX renders your islands on the server, this also ensures that initial values are already included in the server-rendered HTML.
##### Text input example
For a text input field you simply add the `value` attribute and pass the field's input value:
```tsx
{(field) => }
```
##### Exception for files
The HTML `` element is an exception because it cannot be controlled. However, you have the possibility to control the UI around it. For inspiration you can use the code of our [`FileInput`](https://github.com/open-circle/formisch/blob/main/playgrounds/honox/app/components/FileInput.tsx) component from our [playground](/playground/special/).
#### Numbers and dates
Fields defined with a `v.number()` or `v.date()` schema need extra steps to be controlled, because the `` element natively understands only strings as value.
##### Number input example
An `` only reads and writes strings. So if you spread `field.props` onto it, `field.input` holds a string like `"123"`, even though it is typed as `number`. To store a real number, override the change handler with one that converts the value via `valueAsNumber` and forwards it to `field.onChange`.
You also have to handle `NaN`. While typing a floating point number, the value can briefly be `NaN`, for example right after typing `1.`. If you don't catch this for the displayed value, the input is cleared. It is best to encapsulate both in a separate component as described in the [input components](/honox/guides/input-components.md) guide.
```tsx
import type { FieldElementProps } from '@formisch/honox';
import { useMemo, useRef } from 'hono/jsx';
interface NumberInputProps extends Omit {
label?: string;
placeholder?: string;
input: number | undefined;
errors: [string, ...string[]] | null;
required?: boolean;
onChange: (value: number | undefined) => void;
}
export function NumberInput({
label,
input,
errors,
name,
onChange,
...inputProps
}: NumberInputProps) {
const prevValue = useRef(undefined);
// Get value, keeping previous value if NaN
const value = useMemo(() => {
if (typeof input === 'number' && !Number.isNaN(input)) {
prevValue.current = input;
return input;
}
return prevValue.current;
}, [input]);
return (
);
}
```
Pass `field.onChange` after spreading `field.props` so it overrides the native handler.
```tsx
{(field) => (
)}
```
##### Date input example
The same applies to dates. An `` works with `yyyy-mm-dd` strings, while you usually want to store a `Date`. So you convert in both directions: format the `Date` for display, and parse the entered string back with `valueAsDate` before storing it.
```tsx
import type { FieldElementProps } from '@formisch/honox';
import { useMemo } from 'hono/jsx';
interface DateInputProps extends Omit {
label?: string;
placeholder?: string;
input: Date | undefined;
errors: [string, ...string[]] | null;
required?: boolean;
onChange: (value: Date | undefined) => void;
}
export function DateInput({
label,
input,
errors,
name,
onChange,
...inputProps
}: DateInputProps) {
// Transform date to string
const value = useMemo(
() =>
input && !Number.isNaN(input.getTime())
? input.toISOString().split('T', 1)[0]
: '',
[input]
);
return (
);
}
```
As with the number input, pass `field.onChange` after the spread to override the native handler.
```tsx
{(field) => (
)}
```
#### Custom inputs and component libraries
Some component libraries don't expose the underlying native HTML element, which means you cannot spread `field.props` onto them. For these cases, use `field.onChange` to set the value programmatically.
```tsx
import { DatePicker } from 'some-component-library';
{(field) => (
field.onChange(newDate)}
/>
)}
;
```
This is useful for:
- **Component libraries** that wrap native elements without exposing them
- **Complex custom inputs** like date pickers, rich text editors, or color pickers
The `field.onChange` method updates the field value and triggers validation, just like a native input would.
#### Next steps
Now that you understand controlled fields, you can explore more advanced topics like [nested fields](/honox/guides/nested-fields.md) and [field arrays](/honox/guides/field-arrays.md) to handle complex form structures.
### Nested fields
To add a little more structure to a complex form or to match the values of the form to your database, you can also nest your form fields in objects as deep as you like.
#### Schema definition
For example, in the schema below, the first and last name are grouped under the object with the key `name`.
```tsx
import * as v from 'valibot';
const ContactSchema = v.object({
name: v.object({
first: v.pipe(v.string(), v.nonEmpty()),
last: v.pipe(v.string(), v.nonEmpty()),
}),
email: v.pipe(v.string(), v.email()),
message: v.pipe(v.string(), v.nonEmpty()),
});
```
#### Path array
When creating a nested field, use an array with multiple elements for the `path` property to refer to the nested field. The array represents the path through the nested structure.
```tsx
{(field) => }
```
If you're using TypeScript, your editor will provide autocompletion for the path based on your schema structure.
#### Deep nesting
You can nest objects as deeply as needed. Simply extend the path array with additional keys:
```tsx
const ProfileSchema = v.object({
user: v.object({
address: v.object({
street: v.string(),
city: v.string(),
country: v.string(),
}),
}),
});
// Access deeply nested field
{(field) => }
;
```
#### Type safety
The path array is fully type-safe. TypeScript will validate that each element in your path corresponds to a valid key in your schema structure, providing autocompletion and compile-time errors if you reference a non-existent path.
### Dirty fields
When a user edits a form, Formisch tracks which fields have changed since they were initialized. The library exposes three methods for working with that dirty state: [`getDirtyInput`](/methods/api/getDirtyInput.md), [`getDirtyPaths`](/methods/api/getDirtyPaths.md), and [`pickDirty`](/methods/api/pickDirty.md). This guide explains what each one is for, when to reach for it, and why `pickDirty` exists alongside the others.
#### What "dirty" means
A field is dirty when its current input differs from its start input. The form starts clean. As the user types, only the fields they touch flip to dirty. Resetting a field (or resetting the form with a new `initialInput`) clears the dirty flag.
> The dirty flag is per-field. Editing a deeply nested value does not flip its ancestors' flags. The dirty-extraction methods walk the tree internally to find dirty descendants, which is why all three methods do the same kind of work under the hood.
#### The three methods
##### `getDirtyInput`
Returns the dirty subtree of the form's input. Use it when you want to ship the values the user typed — typically for a PATCH endpoint that accepts the same shape as your form input.
```tsx
import {
Form,
getDirtyInput,
type SubmitHandler,
useForm,
} from '@formisch/honox';
import * as v from 'valibot';
const UserSchema = v.object({
name: v.pipe(v.string(), v.nonEmpty()),
email: v.pipe(v.string(), v.email()),
});
interface EditProfileProps {
user: v.InferInput & { id: string };
}
export default function EditProfile({ user }: EditProfileProps) {
const profileForm = useForm({
schema: UserSchema,
initialInput: user,
});
const onSubmit: SubmitHandler = async () => {
const dirty = getDirtyInput(profileForm);
if (dirty) {
await api.patchUser(user.id, dirty);
}
};
return (
{/* fields */}
);
}
```
If only `email` was edited, `dirty` is `{ email: 'new@example.com' }`. If nothing was edited, it is `undefined`.
Because dirty state is bound to the form input, `getDirtyInput` always returns the raw user input. Schema transformations such as `v.trim()` or `v.toNumber()` are not applied to the returned values. When you need the validated and transformed output instead, reach for `pickDirty` (see [below](#why-pickdirty-exists)).
##### `getDirtyPaths`
Returns a list of paths to dirty fields. Use it for logging, telemetry, or driving a custom walker over the form's dirty state.
```tsx
import { getDirtyPaths } from '@formisch/honox';
const dirtyPaths = getDirtyPaths(profileForm);
// e.g. [['email'], ['user', 'name']]
```
Arrays are atomic: only the array's own path is returned, never the paths of individual items.
##### `pickDirty`
Filters a user-supplied value down to its dirty parts using the form's dirty mask. Use it when you have a separately-derived value — most commonly the validated and transformed output from a submit handler — and you want only its dirty parts.
```tsx
import { pickDirty, type SubmitHandler } from '@formisch/honox';
const onSubmit: SubmitHandler = async (output) => {
const dirty = pickDirty(profileForm, { from: output });
if (dirty) {
await api.update(dirty);
}
};
```
#### Why `pickDirty` exists
Dirty state is tracked against the form **input** — the raw values the user typed. But Valibot schemas can transform that input into a different output shape before it reaches your submit handler:
```tsx
import * as v from 'valibot';
const Schema = v.object({
name: v.pipe(v.string(), v.trim()),
age: v.pipe(v.string(), v.toNumber()),
});
```
After validation, `output.name` has whitespace trimmed and `output.age` is a number. [`getDirtyInput`](/methods/api/getDirtyInput.md) would give you the raw strings, because the dirty state is bound to the form input — not to the validated output.
[`pickDirty(form, { from: output })`](/methods/api/pickDirty.md) is the bridge: it walks the form's dirty tree as a structural mask and reads the corresponding values from the output you supply. The result preserves the transformed types.
```tsx
import { pickDirty, type SubmitHandler } from '@formisch/honox';
const onSubmit: SubmitHandler = async (output) => {
// output.age is a number — pickDirty preserves that.
const dirty = pickDirty(form, { from: output });
if (dirty) {
await api.update(dirty);
}
};
```
If the schema reshapes the output entirely — for example, by combining several input fields into a single output value — `pickDirty` returns `undefined` because the shape no longer aligns with the form's input shape.
#### Atomic arrays
All three methods treat arrays as atomic. When any descendant of an array is dirty, the full array is returned (or its own path, for `getDirtyPaths`). The methods never produce sparse arrays.
Sparse arrays don't round-trip safely. Serializers compact them, `JSON.stringify` writes `null` for holes, and indices lose positional meaning. Returning the full current array preserves order and lets the server treat the array as a complete replacement.
This differs from objects, where clean keys are omitted. Objects model keyed dictionaries; arrays model ordered sequences.
#### Common patterns
The snippets below assume `form` is your form store (the result of [`useForm`](/honox/api/useForm.md)).
##### Skip submission when nothing changed
The form's `isDirty` flag is the cheapest way to ask "is anything dirty?" — it short-circuits on the first dirty field it finds and doesn't allocate. Use it when all you need is the yes/no answer. If you actually consume the output of `getDirtyInput`, `getDirtyPaths`, or `pickDirty`, call that method directly — its return value already signals "nothing dirty", so checking `form.isDirty` first would just walk the tree twice.
```tsx
import type { SubmitHandler } from '@formisch/honox';
const onSubmit: SubmitHandler = async (output) => {
if (!form.isDirty) {
return;
}
await api.update(output);
};
```
##### Send only the dirty raw input
```tsx
import { getDirtyInput } from '@formisch/honox';
const dirty = getDirtyInput(form);
if (dirty) {
await api.patch(dirty);
}
```
##### Send only the dirty validated output
```tsx
import { pickDirty, type SubmitHandler } from '@formisch/honox';
const onSubmit: SubmitHandler = async (output) => {
const dirty = pickDirty(form, { from: output });
if (dirty) {
await api.update(dirty);
}
};
```
#### Performance
All three methods walk the form's field tree and call `getFieldBool` recursively to skip clean subtrees. Cost is effectively linear in field count for typical balanced forms (shallow and wide) and degrades toward `O(N²)` for deeply nested trees with few siblings at each level.
Call these methods from submit or blur handlers — not from tight reactive loops on every keystroke. For very large or deeply nested forms (thousands of fields), profile before binding them to high-frequency events.
### Field arrays
Dynamically generating form fields from an array introduces additional challenges when items are added, removed, swapped, or moved. Formisch provides the [`FieldArray`](/honox/api/FieldArray.md) component, which works with its array methods to make these forms straightforward.
> In our [playground](/playground/todos/) you can take a look at a form with a field array and test it out.
#### Create a field array
##### Schema definition
In the following example we create a field array for a todo form with the following schema:
```tsx
import * as v from 'valibot';
const TodoFormSchema = v.object({
heading: v.pipe(v.string(), v.nonEmpty()),
todos: v.pipe(
v.array(
v.object({
label: v.pipe(v.string(), v.nonEmpty()),
deadline: v.pipe(v.string(), v.nonEmpty()),
})
),
v.nonEmpty(),
v.maxLength(10)
),
});
```
##### FieldArray component
To dynamically generate the form fields for the todos, you use the [`FieldArray`](/honox/api/FieldArray.md) component in combination with JavaScript's `Array.prototype.map()` method. The field array provides an `items` array of unique string identifiers which are used to detect when an item is added, moved, or removed.
```tsx
import { Field, FieldArray, Form, useForm } from '@formisch/honox';
export default function TodoPage() {
const todoForm = useForm({
schema: TodoFormSchema,
initialInput: {
heading: '',
todos: [{ label: '', deadline: '' }],
},
});
return (
);
}
```
##### Path array with index
As with [nested fields](/honox/guides/nested-fields.md), you use an array for the `path` property. The key difference is that you include the index from the `map()` function to specify which array item you're referencing. This ensures the paths update correctly when items are added, moved, or removed.
```tsx
{(field) => }
```
#### Use array methods
Now you can use the [`insert`](/methods/api/insert.md), [`move`](/methods/api/move.md), [`remove`](/methods/api/remove.md), [`replace`](/methods/api/replace.md), and [`swap`](/methods/api/swap.md) methods to make changes to the field array. These methods automatically take care of rearranging all the fields.
##### Insert method
Add a new item to the array:
```tsx
import { insert } from '@formisch/honox';
;
```
The `at` option can be used to specify the index where the item should be inserted. If not provided, the item is added to the end of the array.
##### Remove method
Remove an item from the array:
```tsx
import { remove } from '@formisch/honox';
;
```
##### Move method
Move an item from one position to another:
```tsx
import { move } from '@formisch/honox';
;
```
##### Swap method
Swap two items in the array:
```tsx
import { swap } from '@formisch/honox';
;
```
##### Replace method
Replace an item with new data:
```tsx
import { replace } from '@formisch/honox';
;
```
#### Nested field arrays
If you need to nest multiple field arrays, the path array syntax makes it straightforward. Simply extend the path with additional array indices:
```tsx
const NestedSchema = v.object({
categories: v.array(
v.object({
name: v.string(),
items: v.array(
v.object({
title: v.string(),
})
),
})
),
});
{(categoryArray) => (
<>
{categoryArray.items.map((categoryItem, categoryIndex) => (
))}
>
)}
;
```
You can nest field arrays as deeply as you like. You will also find a suitable example of this in our [playground](/playground/nested/).
#### Field array validation
As with fields, you can validate field arrays using Valibot's array validation functions. For example, to limit the length of the array:
```tsx
const TodoFormSchema = v.object({
todos: v.pipe(
v.array(
v.object({
label: v.string(),
deadline: v.string(),
})
),
v.minLength(1),
v.maxLength(10)
),
});
```
The validation errors for the field array itself are available in `fieldArray.errors` and can be displayed alongside the array.
### TypeScript
Since the library is written in TypeScript and we put a lot of emphasis on the development experience, you can expect maximum TypeScript support. Types are automatically inferred from your [Valibot schemas](/honox/guides/define-your-form.md), providing type safety throughout your forms.
#### Type inference
Formisch uses Valibot's type inference to automatically derive TypeScript types from your schemas. You don't need to define separate types—they're inferred automatically.
```tsx
import { Field, Form, useForm } from '@formisch/honox';
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 LoginPage() {
const loginForm = useForm({
schema: LoginSchema,
});
// TypeScript knows the form structure from the schema
// loginForm is of type FormStore
return (
{
// output is fully typed based on LoginSchema
console.log(output.email); // ✓ Type-safe
console.log(output.username); // ✗ TypeScript error
}}
>
{/* Form fields */}
);
}
```
##### Input and output types
Valibot schemas can have different input and output types when using transformations. Formisch provides proper typing for both.
```tsx
import { Field, Form, useForm } from '@formisch/honox';
import * as v from 'valibot';
const ProfileSchema = v.object({
age: v.pipe(
v.string(), // Input type: string (from HTML input)
v.transform((input) => Number(input)), // Output type: number
v.number()
),
birthDate: v.pipe(
v.string(), // Input type: string (ISO date string)
v.transform((input) => new Date(input)), // Output type: Date
v.date()
),
});
export default function ProfilePage() {
const profileForm = useForm({
schema: ProfileSchema,
});
return (
{
// output is: { age: number; birthDate: Date }
console.log(output.age); // number
console.log(output.birthDate); // Date
}}
>
{(field) => (
// field.input is string
)}
{(field) => (
// field.input is string
)}
);
}
```
##### Type-safe paths
Field paths are fully type-checked. TypeScript will provide autocompletion and catch invalid paths at compile time.
```tsx
const UserSchema = v.object({
profile: v.object({
name: v.object({
first: v.string(),
last: v.string(),
}),
email: v.string(),
}),
});
const userForm = useForm({ schema: UserSchema });
// ✓ Valid paths - TypeScript provides autocompletion
{() => <>{/* ... */}>}{() => <>{/* ... */}>}
// ✗ Invalid paths - TypeScript error
{() => <>{/* ... */}>}{() => <>{/* ... */}>}
```
#### Type-safe props
To pass your form to another component via props, you can use the [`FormStore`](/honox/api/FormStore.md) type along with your schema type to get full type safety.
```tsx
import { Form, type FormStore, useForm } from '@formisch/honox';
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 LoginPage() {
const loginForm = useForm({
schema: LoginSchema,
});
return ;
}
type FormContentProps = {
of: FormStore;
};
function FormContent(props: FormContentProps) {
return (
console.log(output)}>
{/* Form fields */}
);
}
```
#### Generic field components
You can create generic field components with proper TypeScript typing using the [`FormStore`](/honox/api/FormStore.md) type with Valibot's `GenericSchema`.
```tsx
import { type FormStore, useField } from '@formisch/honox';
import type { FC } from 'hono/jsx';
import * as v from 'valibot';
type EmailInputProps = {
of: FormStore>;
};
const EmailInput: FC = (props) => {
const field = useField(props.of, { path: ['email'] });
return (
{field.errors &&
{field.errors[0]}
}
);
};
```
The `v.GenericSchema<{ email: string }>` type ensures that the form passed to `EmailInput` must have an `email` field of type `string`. TypeScript will catch any type mismatches at compile time.
#### Available types
Most types you need can be imported from `@formisch/honox`. You can find all available types in our [API reference](/honox/api/).
- [`FormStore`](/honox/api/FormStore.md) - The form store type
- [`FieldStore`](/honox/api/FieldStore.md) - The field store type
- [`FieldArrayStore`](/honox/api/FieldArrayStore.md) - The field
array store type
- [`ValidPath`](/core/api/ValidPath.md) - Type for valid field
paths
- [`ValidArrayPath`](/core/api/ValidArrayPath.md) - Type for
valid array field paths
- [`FormSchema`](/core/api/FormSchema.md) - Form schema type
required at the root
- [`Schema`](/core/api/Schema.md) - Base schema type from Valibot
- [`SubmitHandler`](/core/api/SubmitHandler.md) - Type for submit
handlers without the event
- [`SubmitEventHandler`](/core/api/SubmitEventHandler.md) - Type
for submit handlers with the event
### Architecture
You don't need to read this guide to use Formisch. It's here for people who want to understand why the bundle stays small, why updates are fine-grained, and how the same library can support HonoX, React, React Native, Angular, SolidJS, Vue, Svelte, Preact and Qwik without forking the codebase. Each of those properties falls out of a few deliberate architectural choices.
#### Three packages, three responsibilities
Formisch is structured as three packages, each with a clear role:
- [`@formisch/core`](https://github.com/open-circle/formisch/tree/main/packages/core) is the framework-agnostic foundation. It contains the form and field store types, the recursive store builder that mirrors your Valibot schema, and the validation orchestration.
- [`@formisch/methods`](https://github.com/open-circle/formisch/tree/main/packages/methods) exposes the form operations as standalone, tree-shakeable functions: [`setInput`](/methods/api/setInput.md), [`validate`](/methods/api/validate.md), [`reset`](/methods/api/reset.md), [`insert`](/methods/api/insert.md), [`move`](/methods/api/move.md), [`focus`](/methods/api/focus.md) and so on. Each method lives in its own file and only imports the core helpers it actually needs.
- [`@formisch/honox`](https://github.com/open-circle/formisch/tree/main/frameworks/honox) is the thin user-facing layer. It provides the [`useForm`](/honox/api/useForm.md), [`useField`](/honox/api/useField.md) and [`useFieldArray`](/honox/api/useFieldArray.md) hooks, the [`
`](/honox/api/Form.md), [``](/honox/api/Field.md) and [``](/honox/api/FieldArray.md) components, and re-exports every method from `@formisch/methods` so you only import from a single package.
```
@formisch/honox
├─ useForm,
, , … (HonoX-specific reactive layer)
└─ re-exports @formisch/methods/honox (focus, reset, validate, …)
└─ @formisch/core/honox (createFormStore, types, signals)
└─ framework adapter (createSignal, batch, untrack, createId)
```
The modular architecture is what makes Formisch tree-shakeable. The core stays small, and every additional capability (`setInput`, `validate`, `reset`, `insert`, `move`, `focus` and the rest) is exported as its own function. A form that only imports `useForm`, `
` and `` ships nothing else, even though `@formisch/honox` re-exports every method.
#### One core, one adapter per framework
The core package is framework-agnostic, but it still needs _some_ reactivity primitive to build on. Formisch handles this by providing a subpath export for every supported framework (`@formisch/core/honox`, `@formisch/core/react`, `@formisch/core/solid`, …), all built from the same source. The only file that differs between them [is a small adapter](https://github.com/open-circle/formisch/blob/main/packages/core/src/framework/index.ts) that exports four functions: `createSignal`, `batch`, `untrack` and `createId`.
The swap happens at build time, not at runtime. A small [rolldown plugin](https://github.com/open-circle/formisch/blob/main/packages/core/tsdown.config.ts) sits in front of import resolution: whenever the bundler resolves an import of `./framework/index.ts`, the plugin looks for a sibling `./framework/index.honox.ts` (or `.react.ts`, `.solid.ts`, …) and, if it exists, redirects the import there. The build runs once per framework, and each run produces a self-contained bundle with the framework-specific adapter inlined.
hono/jsx follows React's rendering model, and neither has a native signal primitive. [The HonoX adapter](https://github.com/open-circle/formisch/blob/main/packages/core/src/framework/index.honox.ts) therefore re-exports [the React adapter](https://github.com/open-circle/formisch/blob/main/packages/core/src/framework/index.react.ts), which implements a small pub/sub system from scratch that exposes the `Signal` shape:
```ts
export function createSignal(value: T): Signal {
const subscribers = new Set();
return {
get value() {
if (listener) {
subscribers.add(listener);
listener[1].add(subscribers);
}
return value;
},
set value(newValue: T) {
if (newValue !== value) {
value = newValue;
// notify subscribers …
}
},
};
}
```
A companion [`useSignals`](https://github.com/open-circle/formisch/blob/main/frameworks/honox/src/hooks/useSignals/useSignals.ts) hook in the HonoX wrapper registers your component as a listener and forces a re-render when any signal it read changes. Since hono/jsx neither runs effects nor re-renders components on the server, signals are only tracked in the browser. During server-side rendering, the form simply renders its initial state. For frameworks that already have native signals (Solid, Preact, Vue, Svelte, Qwik), the adapter is a thin wrapper around the framework's own primitive, sometimes just a single re-export. Every other file in `@formisch/core` only ever imports `createSignal`, `batch`, `untrack` and `createId`, never a framework directly. That's why the same [`createFormStore`](https://github.com/open-circle/formisch/blob/main/packages/core/src/form/createFormStore/createFormStore.ts) code path can power every framework binding without a single conditional.
This is what makes Formisch different from most framework-agnostic libraries. The usual approach is to ship a runtime abstraction (a custom store, an observer protocol, a subscription layer) that lives in the bundle and has to be bridged into each framework. Formisch ships nothing like that. Where the framework has native signals, Formisch uses them directly; where it doesn't, the adapter is just enough pub/sub to drive re-renders. Either way, there is no extra layer between your form state and the framework's reactivity, and your bundle pays no overhead for portability. In HonoX, form state lives in the same minimal pub/sub the wrapper already needs to subscribe components, with no second abstraction on top.
That Formisch is framework-agnostic is not a trade-off you make. It's a benefit you get. A bug surfaced by a Svelte user is a bug fixed for HonoX, React, Solid, Vue and everyone else. An improvement to the recursive store builder, the validation orchestration or any individual method lands across every binding at once. There is one library to maintain, not one per framework, and every community pulls the rest forward.
#### State lives in signals
Every piece of reactive state in Formisch is a [`Signal`](https://github.com/open-circle/formisch/blob/main/packages/core/src/types/signal/signal.ts) with the same minimal interface:
```ts
interface Signal {
get value(): T;
set value(nextValue: T): void;
}
```
Reads track, writes notify. There is no central store object that gets diffed and no equality check on the consumer side. Each field carries its own signals for `errors`, `isTouched`, `isEdited`, `isDirty`, plus an `input` signal for value fields and a `children` collection for arrays and objects. When you call `setInput(form, { path: ['email'], input: 'a@b.c' })`, only the `input` signal of that one field is written, so only the components that read that one signal (typically a single ``) re-render.
This is the source of the fine-grained reactivity. The shape of the store is pre-allocated at form creation, matching the shape of your Valibot schema, so methods never have to look up paths in a generic object. They walk a typed tree of stores and update individual signals.
#### What happens when you call `useForm`
When you call [`useForm`](/honox/api/useForm.md), three layers cooperate to produce the form store you receive.
```tsx
import { useForm } from '@formisch/honox';
import * as v from 'valibot';
const LoginSchema = v.object({
email: v.pipe(v.string(), v.email()),
password: v.pipe(v.string(), v.minLength(8)),
});
function LoginForm() {
const loginForm = useForm({ schema: LoginSchema });
// …
}
```
First, the [HonoX wrapper](https://github.com/open-circle/formisch/blob/main/frameworks/honox/src/hooks/useForm/useForm.ts) hands the configuration to the core function [`createFormStore`](https://github.com/open-circle/formisch/blob/main/packages/core/src/form/createFormStore/createFormStore.ts) inside a `useMemo`, together with a parse closure that captures your schema:
```ts
const internalFormStore = useMemo(
() =>
createFormStore(config, (input) => v.safeParseAsync(config.schema, input)),
[]
);
```
Next, `createFormStore` calls [`initializeFieldStore`](https://github.com/open-circle/formisch/blob/main/packages/core/src/field/initializeFieldStore/initializeFieldStore.ts), which walks your Valibot schema recursively and builds the internal store hierarchy:
- A `v.object(...)` becomes an `InternalObjectStore` with a `children` record keyed by field name.
- A `v.array(...)` or `v.tuple(...)` becomes an `InternalArrayStore` with a `children` list and its own `initialInput`, `startInput` and `input` signals so it can track structural changes.
- A leaf like `v.string()` or `v.number()` becomes an `InternalValueStore` with an `input` signal that holds the current field value.
- Wrapper schemas such as `v.optional`, `v.nullable`, `v.nonNullable` and `v.lazy` are unwrapped and recursed into.
- `v.union`, `v.variant` and `v.intersect` initialize every option into the same store, which is what lets you address discriminated fields with a single field path.
At every level, the same four signals are created: `errors`, `isTouched`, `isEdited` and `isDirty`. Once the field tree is built, three more signals are added to the form root: `isSubmitting`, `isSubmitted` and `isValidating`.
Finally, the HonoX wrapper builds the public form store you actually receive. It's a small object that sits on top of the internal store: form-level signals are exposed through getters that read `internalFormStore.isSubmitting.value` and friends, while derived state like `isTouched`, `isEdited`, `isDirty` and `isValid` is computed on demand from the field tree. The whole object is memoized so it survives re-renders:
```ts
return useMemo(
() => ({
[INTERNAL]: internalFormStore,
get isSubmitting() {
return internalFormStore.isSubmitting.value;
},
get isTouched() {
return getFieldBool(internalFormStore, 'isTouched');
},
// …
}),
[internalFormStore]
);
```
At the very top of the hook body, before any of this, the wrapper calls `useSignals()` to bridge the signals into the render cycle of hono/jsx: it registers the component as a listener so any signal read by the getters above will trigger a re-render when it changes.
The internal store is tucked away behind the [`INTERNAL`](https://github.com/open-circle/formisch/blob/main/packages/core/src/values.ts) key. Methods like `setInput(form, …)` reach in via `form[INTERNAL]` to mutate signals, while regular form code uses the public form store. This separation is what keeps the public API small and stable even as the internal shape evolves.
#### Why this design pays off
Bundle size is the obvious payoff. The others are why the design is worth talking about:
- **Small bundle size.** Because methods are individual modules re-exported from [`@formisch/honox`](https://github.com/open-circle/formisch/tree/main/frameworks/honox), a form that uses only [`useForm`](/honox/api/useForm.md) and [``](/honox/api/Field.md) never pulls in [`validate`](/methods/api/validate.md), [`reset`](/methods/api/reset.md), [`insert`](/methods/api/insert.md) or the other operations. Tree-shaking gets to do real work.
- **Fine-grained reactivity.** The store shape is pre-allocated at form creation, with one signal per piece of state. Methods write `.value` directly, and only the components subscribed to that specific signal re-render. Updating one field does not invalidate the rest.
- **Framework portability.** The entire library is parameterized over a four-function adapter. Adding a new framework means writing a small adapter that maps `createSignal`, `batch`, `untrack` and `createId` onto its primitives.
- **Type safety.** Your Valibot schema is the single source of truth. It drives both runtime parsing and the inferred TypeScript types for paths, inputs and outputs, so there is no second declaration to keep in sync.
#### Build on the core
Use `@formisch/honox/internals` to access the framework-specific core from your application or library. The [internals guide](/honox/guides/internals.md) shows how to create custom methods and explains what to consider when building reactive bindings and components.
#### Further reading
To see how these design choices stack up against React Hook Form and TanStack Form, head to the [comparison](/honox/guides/comparison.md) guide. For a closer look at how Valibot's inference flows into your editor, see [TypeScript](/honox/guides/typescript.md). And if you want to read the recursive walker, the adapter implementations, or how individual methods are written, the full source is on [GitHub](https://github.com/open-circle/formisch).
### Internals
Formisch exposes its core through `@formisch/honox/internals`. You can use these functions and types to build methods, reactive bindings and components for behavior that Formisch does not provide out of the box. Coding agents can use the same building blocks as the library itself.
#### Versioning
**Internals are not covered by Formisch's semantic versioning guarantees.** Breaking changes to their functions, types and store structure can occur in any release, including minor and patch releases. Review your use of internals when upgrading Formisch, and test validation, reactivity and cleanup for the behavior your extension implements.
#### Import the core
```ts
import {
type BaseFormStore,
getFieldStore,
INTERNAL,
setFieldInput,
} from '@formisch/honox/internals';
```
This entry point re-exports the entire HonoX-specific core, including store types, field and array helpers, validation utilities and the reactivity adapter. It is included in `@formisch/honox`, so no additional package is needed. Always use the internals from the same framework package as your form so they share its reactivity implementation.
The regular `@formisch/honox` entry point provides the ready-made methods and framework bindings. The internals entry point gives you access to the lower-level operations and store structure described in the [architecture guide](/honox/guides/architecture.md).
#### Implement a method
The existing [`setInput`](/methods/api/setInput.md) method shows how these building blocks work together. This simplified implementation updates a single field and reuses `SetFieldInputConfig` to infer the path and input types from your schema:
```ts
import type { SetFieldInputConfig } from '@formisch/honox';
import {
type BaseFormStore,
batch,
type FormSchema,
getFieldStore,
INTERNAL,
type RequiredPath,
setFieldInput,
validateIfRequired,
} from '@formisch/honox/internals';
/**
* Sets a field's input and validates it if required.
*/
export function setInput<
TSchema extends FormSchema,
TFieldPath extends RequiredPath,
>(
form: BaseFormStore,
config: SetFieldInputConfig
): void {
batch(() => {
const internalFormStore = form[INTERNAL];
const internalFieldStore = getFieldStore(internalFormStore, config.path);
if (internalFieldStore) {
setFieldInput(internalFormStore, config.path, config.input);
validateIfRequired(internalFormStore, internalFieldStore, 'input');
}
});
}
```
Pass an existing form store, a field path and its new input to this method:
```ts
setInput(loginForm, { path: ['email'], input: 'jane@example.com' });
```
`INTERNAL` is the key for accessing the internal store through `form[INTERNAL]`. `getFieldStore` can return `undefined` when a dynamic array item no longer exists, so check the result before updating the field.
`setFieldInput` updates the input and maintains touched, edited and dirty state. Writing directly to an input signal bypasses this bookkeeping. `batch` groups signal updates, and `validateIfRequired` triggers validation only when the form's validation settings require it for an input event.
The [full implementation](https://github.com/open-circle/formisch/blob/main/packages/methods/src/setInput/setInput.ts) also supports setting the entire form input and includes the corresponding type overloads. Use the built-in method in your application; this example is a starting point for understanding the core helpers and building your own methods.
#### Build reactive bindings and components
You can also build your own framework bindings on top of the core. These bindings are responsible for subscribing to state changes, registering input elements or refs, handling events and cleaning up when fields unmount. Start with [input components](/honox/guides/input-components.md) if you can compose the existing bindings.
Reading a core signal alone does not subscribe a hono/jsx component. The existing hooks use the internal `useSignals` hook to manage subscriptions and cleanup. It only tracks signals in the browser, as hono/jsx neither runs effects nor re-renders components during server-side rendering. Compose the public hooks where possible, or follow that implementation when building your own subscription layer.
The [HonoX binding source](https://github.com/open-circle/formisch/tree/main/frameworks/honox/src) shows how Formisch handles these responsibilities. The core exposes the helpers those bindings use; it does not automatically supply their framework lifecycle behavior.
#### Work with coding agents
Give your agent this guide, your schema and the behavior you want to build. Ask it to inspect the relevant method or framework binding and the installed type declarations, then test the custom behavior with your form. The [coding agents guide](/honox/guides/coding-agents.md) explains how to access the documentation through our skill, MCP server and Markdown files.
## Hooks (api)
### useForm
Creates a reactive form store from a form configuration. The form store manages form state and provides reactive properties.
```ts
const form = useForm(config);
```
#### Generics
- `TSchema` `extends FormSchema`
#### Parameters
- `config` `FormConfig`
##### Explanation
`useForm` creates a reactive form store that manages form state using the provided Valibot schema. Validation runs when the form is submitted by default, after which fields revalidate on every input, but this can be customized with the `validate` and `revalidate` options.
#### Returns
- `form` `FormStore`
#### Related
The following APIs can be combined with `useForm`.
##### Hooks
[`useField`](/honox/api/useField.md), [`useFieldArray`](/honox/api/useFieldArray.md)
##### Components
[`Field`](/honox/api/Field.md), [`FieldArray`](/honox/api/FieldArray.md), [`Form`](/honox/api/Form.md)
##### Methods
[`focus`](/methods/api/focus.md), [`getDeepErrors`](/methods/api/getDeepErrors.md), [`getErrors`](/methods/api/getErrors.md), [`getInput`](/methods/api/getInput.md), [`handleSubmit`](/methods/api/handleSubmit.md), [`insert`](/methods/api/insert.md), [`move`](/methods/api/move.md), [`remove`](/methods/api/remove.md), [`replace`](/methods/api/replace.md), [`reset`](/methods/api/reset.md), [`setErrors`](/methods/api/setErrors.md), [`setInput`](/methods/api/setInput.md), [`submit`](/methods/api/submit.md), [`swap`](/methods/api/swap.md), [`validate`](/methods/api/validate.md)
### useField
Creates a reactive field store for a specific field within a form store.
```ts
const field = useField(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Parameters
- `form` `FormStore`
- `config` `UseFieldConfig`
##### Explanation
With `useField` you create a reactive field store for a specific field path within your form. The field store provides reactive access to the field's input value, validation errors, touched state, and more. It also includes a `props` object that you can spread onto an HTML input or textarea element to automatically handle user interactions like focus, change, and blur events.
#### Returns
- `field` `FieldStore`
#### Related
The following APIs can be combined with `useField`.
##### Hooks
[`useForm`](/honox/api/useForm.md), [`useFieldArray`](/honox/api/useFieldArray.md)
##### Components
[`Field`](/honox/api/Field.md), [`FieldArray`](/honox/api/FieldArray.md), [`Form`](/honox/api/Form.md)
##### Methods
[`focus`](/methods/api/focus.md), [`getErrors`](/methods/api/getErrors.md), [`getInput`](/methods/api/getInput.md), [`setErrors`](/methods/api/setErrors.md), [`setInput`](/methods/api/setInput.md), [`validate`](/methods/api/validate.md)
### useFieldArray
Creates a reactive field array store for a specific field array within a form store.
```ts
const fieldArray = useFieldArray(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldArrayPath` `extends RequiredPath`
#### Parameters
- `form` `FormStore`
- `config` `UseFieldArrayConfig`
##### Explanation
With `useFieldArray` you create a reactive field array store for a specific array field within your form. The field array store provides reactive access to the array items (by their IDs), validation errors, touched state, and more. This is particularly useful when working with dynamic arrays of fields that users can add or remove.
#### Returns
- `fieldArray` `FieldArrayStore`
#### Related
The following APIs can be combined with `useFieldArray`.
##### Hooks
[`useForm`](/honox/api/useForm.md), [`useField`](/honox/api/useField.md)
##### Components
[`Field`](/honox/api/Field.md), [`FieldArray`](/honox/api/FieldArray.md), [`Form`](/honox/api/Form.md)
##### Methods
[`focus`](/methods/api/focus.md), [`getDeepErrors`](/methods/api/getDeepErrors.md), [`getErrors`](/methods/api/getErrors.md), [`getInput`](/methods/api/getInput.md), [`insert`](/methods/api/insert.md), [`move`](/methods/api/move.md), [`remove`](/methods/api/remove.md), [`replace`](/methods/api/replace.md), [`reset`](/methods/api/reset.md), [`setErrors`](/methods/api/setErrors.md), [`setInput`](/methods/api/setInput.md), [`swap`](/methods/api/swap.md), [`validate`](/methods/api/validate.md)
## Components (api)
### Form
Form component that manages form submission and applies internal state. It wraps a native form element and calls the provided handler with the validated form output and the submit event.
```tsx
children
```
#### Generics
- `TSchema` `extends FormSchema`
#### Properties
- `of` `FormStore`
- `children` `Child`
- `onSubmit` `SubmitEventHandler`
##### Explanation
The `Form` component wraps a native HTML `
` element and provides form submission handling. It automatically prevents the default form submission behavior and calls the `onSubmit` handler when the form is submitted and validation succeeds.
The component uses the `of` prop to reference the form store and renders the `children` elements within the form. It also sets `novalidate` on the form element to disable browser validation, as Formisch handles validation internally.
#### Related
##### Hooks
[`useForm`](/honox/api/useForm.md)
##### Components
[`Field`](/honox/api/Field.md), [`FieldArray`](/honox/api/FieldArray.md)
##### Methods
[`focus`](/methods/api/focus.md), [`getDeepErrors`](/methods/api/getDeepErrors.md), [`getErrors`](/methods/api/getErrors.md), [`handleSubmit`](/methods/api/handleSubmit.md), [`reset`](/methods/api/reset.md), [`setErrors`](/methods/api/setErrors.md), [`setInput`](/methods/api/setInput.md), [`submit`](/methods/api/submit.md), [`validate`](/methods/api/validate.md)
### Field
Headless form field component that provides reactive properties and state. The field component takes a form store, path to field, and a render function that receives a field store to display field state and handle user interactions.
```tsx
children
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Properties
- `of` `FormStore`
- `path` `ValidPath, TFieldPath>`
- `children` `(store: FieldStore) => JSX.Element`
##### Explanation
The `Field` component is a headless component that doesn't render any UI itself. Instead, it provides a render prop function via the `children` prop that receives a [`FieldStore`](/honox/api/FieldStore.md) containing all the reactive state and props needed to render a form field.
The component automatically handles field registration, validation, and state management through the `of` prop that references the form store.
#### Related
##### Hooks
[`useForm`](/honox/api/useForm.md), [`useField`](/honox/api/useField.md)
##### Components
[`Form`](/honox/api/Form.md), [`FieldArray`](/honox/api/FieldArray.md)
### FieldArray
Headless field array component that provides reactive properties and state for managing array fields. The field array component takes a form store, path to array field, and a render function that receives a field array store to manage array items and handle array operations.
```tsx
children
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldArrayPath` `extends RequiredPath`
#### Properties
- `of` `FormStore`
- `path` `ValidArrayPath, TFieldArrayPath>`
- `children` `(store: FieldArrayStore) => JSX.Element`
##### Explanation
The `FieldArray` component is a headless component that doesn't render any UI itself. Instead, it provides a render prop function via the `children` prop that receives a [`FieldArrayStore`](/honox/api/FieldArrayStore.md) containing the reactive state of the array field, such as its item IDs and validation errors. The items themselves are manipulated with the form methods listed below.
The component automatically handles array field registration, validation, and state management through the `of` prop that references the form store.
#### Related
##### Hooks
[`useForm`](/honox/api/useForm.md), [`useFieldArray`](/honox/api/useFieldArray.md)
##### Components
[`Field`](/honox/api/Field.md), [`Form`](/honox/api/Form.md)
##### Methods
[`insert`](/methods/api/insert.md), [`move`](/methods/api/move.md), [`remove`](/methods/api/remove.md), [`replace`](/methods/api/replace.md), [`swap`](/methods/api/swap.md)
## Methods (api)
### focus
Focuses the first focusable input element of a field. This is useful for programmatically setting focus to a specific field, such as after validation errors or user interactions.
```ts
focus(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Parameters
- `form` `BaseFormStore`
- `config` `FocusFieldConfig`
##### Explanation
The `form` parameter is the form store containing the field to focus. The `config` parameter specifies which field to focus using the `path` property.
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`validate`](/methods/api/validate.md), [`setErrors`](/methods/api/setErrors.md)
### getDeepError
Retrieves the first error message of a specific field or the entire form by walking through the field store and all its descendants and stopping at the first field with errors. This is useful for displaying a single error message for a field whose value is a nested structure, such as a rich text editor or tags input.
```ts
const error = getDeepError(form);
const error = getDeepError(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `GetFormDeepErrorConfig | GetFieldDeepErrorConfig | undefined`
##### Explanation
The `form` parameter is the form store to retrieve the error from. The optional `config` parameter scopes the search to a specific field's subtree via a `path` (when omitted, the entire form is walked). The function walks through the field store and all its descendants in depth-first order and stops at the first field with errors, so errors of a field surface before errors of its descendants.
#### Returns
- `result` `string | null`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`getErrors`](/methods/api/getErrors.md), [`getDeepErrors`](/methods/api/getDeepErrors.md), [`getDeepErrorEntry`](/methods/api/getDeepErrorEntry.md), [`validate`](/methods/api/validate.md)
### getDeepErrorEntries
Retrieves the errors of a specific field or the entire form as a list of entries, each pairing the path to a field with its error messages. This is useful for building custom error summaries that link each message back to its field.
```ts
const entries = getDeepErrorEntries(form);
const entries = getDeepErrorEntries(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `GetFormDeepErrorEntriesConfig | GetFieldDeepErrorEntriesConfig | undefined`
##### Explanation
The `form` parameter is the form store to retrieve error entries from. The optional `config` parameter scopes the collection to a specific field's subtree via a `path` (when omitted, the entire form is walked). Each returned entry contains the `path` to a field and its non-empty list of `errors`.
#### Returns
- `result` `DeepErrorEntry>[]`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`getErrors`](/methods/api/getErrors.md), [`getDeepErrors`](/methods/api/getDeepErrors.md), [`getDeepErrorEntry`](/methods/api/getDeepErrorEntry.md), [`validate`](/methods/api/validate.md)
### getDeepErrorEntry
Retrieves the errors of the first erroring field of a specific field or the entire form as an entry pairing the path to the field with its error messages, by walking through the field store and all its descendants and stopping at the first field with errors. This is useful for locating the first erroring descendant of a field whose value is a nested structure while retaining all errors at that path.
```ts
const entry = getDeepErrorEntry(form);
const entry = getDeepErrorEntry(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `GetFormDeepErrorEntryConfig | GetFieldDeepErrorEntryConfig | undefined`
##### Explanation
The `form` parameter is the form store to retrieve the error entry from. The optional `config` parameter scopes the search to a specific field's subtree via a `path` (when omitted, the entire form is walked). The function walks through the field store and all its descendants in depth-first order and stops at the first field with errors, so errors of a field surface before errors of its descendants. The returned entry contains the `path` to the field and its non-empty list of `errors`. Form-level errors are included with an empty path.
#### Returns
- `result` `DeepErrorEntry> | null`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`getErrors`](/methods/api/getErrors.md), [`getDeepError`](/methods/api/getDeepError.md), [`getDeepErrorEntries`](/methods/api/getDeepErrorEntries.md), [`validate`](/methods/api/validate.md)
### getDeepErrors
Retrieves all error messages of a specific field or the entire form by walking through the field store and all its descendants. This is useful for displaying a summary of all validation errors within a section or the whole form.
```ts
const errors = getDeepErrors(form);
const errors = getDeepErrors(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `GetFormDeepErrorsConfig | GetFieldDeepErrorsConfig | undefined`
##### Explanation
The `form` parameter is the form store to retrieve errors from. The optional `config` parameter scopes the collection to a specific field's subtree via a `path` (when omitted, the entire form is walked). The function walks through the field store and all its descendants to collect every validation error message.
#### Returns
- `result` `[string, ...string[]] | null`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`getErrors`](/methods/api/getErrors.md), [`getDeepError`](/methods/api/getDeepError.md), [`getDeepErrorEntries`](/methods/api/getDeepErrorEntries.md), [`validate`](/methods/api/validate.md), [`setErrors`](/methods/api/setErrors.md)
### getDirtyInput
Retrieves only the dirty input values of a specific field or the entire form. Object keys whose subtree contains no dirty descendant are omitted; arrays are treated as atomic and returned in full whenever any descendant is dirty. Returns `undefined` if no field in the inspected subtree is dirty.
```ts
const input = getDirtyInput(form);
const input = getDirtyInput(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `GetFormDirtyInputConfig | GetFieldDirtyInputConfig | undefined`
##### Explanation
The `form` parameter is the form store to retrieve dirty input from. The optional `config` parameter scopes the result to a specific field path - if omitted, returns dirty input from the entire form. The returned value is a partial of the inspected input shape containing only the fields whose `isDirty` flag is set, which is useful for submitting only the values that changed since the start input. Arrays are treated as atomic — when any descendant of an array is dirty, the full current array is returned.
Internally, the function walks the form's field tree and asks `getFieldBool` whether each branch contains a dirty descendant. Because that check is itself recursive, the cost is effectively linear in field count for typical balanced forms (shallow and wide) and degrades toward `O(N²)` for deeply nested trees. Call this method from submit or blur handlers rather than from tight reactive loops on large forms.
#### Returns
- `result` `DeepPartial> | undefined`
#### Related
##### Methods
[`getDirtyPaths`](/methods/api/getDirtyPaths.md), [`getInput`](/methods/api/getInput.md), [`pickDirty`](/methods/api/pickDirty.md), [`reset`](/methods/api/reset.md)
### getDirtyPaths
Returns a list of paths to dirty fields in the form. Object branches are recursed into; arrays are treated as atomic — when any descendant of an array is dirty, only the array's own path is returned.
```ts
const paths = getDirtyPaths(form);
const paths = getDirtyPaths(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `GetFormDirtyPathsConfig | GetFieldDirtyPathsConfig | undefined`
##### Explanation
The `form` parameter is the form store to inspect. The optional `config` parameter scopes the search to a single subtree via its `path` property. Object branches are recursed into and clean keys are omitted; arrays are atomic, so when any descendant of an array is dirty, the result includes the array's own path rather than the paths of its individual items. Returns an empty array if the inspected subtree contains no dirty fields. When `path` targets a dirty array or value field, the result contains that path.
Internally, the function collects the paths in a single recursive walk that visits each field at most once, so the cost is linear in the number of fields in the inspected subtree.
#### Returns
- `result` `DirtyPath>[]`
#### Related
##### Methods
[`getDirtyInput`](/methods/api/getDirtyInput.md), [`getInput`](/methods/api/getInput.md), [`pickDirty`](/methods/api/pickDirty.md)
### getErrors
Retrieves error messages from the form. When called without a config, returns form-level errors. When called with a path, returns errors for that specific field.
```ts
const errors = getErrors(form);
const errors = getErrors(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `GetFormErrorsConfig | GetFieldErrorsConfig | undefined`
##### Explanation
The `form` parameter is the form store to retrieve errors from. The optional `config` parameter specifies which errors to retrieve - either form-level errors (when omitted) or field-specific errors (when a `path` is provided).
#### Returns
- `result` `[string, ...string[]] | null`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`getDeepErrors`](/methods/api/getDeepErrors.md), [`setErrors`](/methods/api/setErrors.md), [`validate`](/methods/api/validate.md)
### getInput
Retrieves the current input value of a specific field or the entire form. Returns a partial object as not all fields may have been set.
```ts
const input = getInput(form);
const input = getInput(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `GetFormInputConfig | GetFieldInputConfig | undefined`
##### Explanation
The `form` parameter is the form store to retrieve input from. The optional `config` parameter specifies which field to get input from - if omitted, returns input from the entire form.
#### Returns
- `result` `PartialValues>`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`setInput`](/methods/api/setInput.md), [`reset`](/methods/api/reset.md)
### handleSubmit
Creates a submit event handler for the form that prevents default browser submission, validates the form input, and calls the provided handler if validation succeeds. This is designed to be used with the form's onsubmit event.
```ts
const result = handleSubmit(form, handler);
```
> In React Native, where there is no form element or submit event, `handleSubmit` returns a plain async function that you call from a button's `onPress` or a text input's `onSubmitEditing` handler. In this case the handler receives only the validated form output — there is no event object.
#### Generics
- `TSchema` `extends FormSchema`
#### Parameters
- `form` `BaseFormStore`
- `handler` `SubmitHandler | SubmitEventHandler`
##### Explanation
The `form` parameter is the form store to handle submission for. The `handler` parameter can be a `SubmitHandler` (no event) or, in the frameworks that submit through a form element, a `SubmitEventHandler` (with event) that gets called with the validated form output if validation succeeds. In React Native only `SubmitHandler` applies, as there is no submit event.
If the input changes, the form is fully reset, or a newer validation or submission starts before validation finishes, the outdated submission does not call its handler.
A newer submission or full form reset also prevents an older handler from changing form errors or `isSubmitting` when it finishes. This does not abort a handler or network request that has already started.
#### Returns
- `result` `(() => Promise) | ((event: SubmitEvent) => Promise)`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`validate`](/methods/api/validate.md), [`focus`](/methods/api/focus.md), [`submit`](/methods/api/submit.md)
### insert
Inserts a new item into a field array at the specified index. All items at or after the insertion point are shifted up by one index.
```ts
insert(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldArrayPath` `extends RequiredPath`
#### Parameters
- `form` `BaseFormStore`
- `config` `InsertConfig`
##### Explanation
The `form` parameter is the form store containing the field array. The `config` parameter specifies the path to the field array, the index where to insert the new item, and optional initial input values for the new item.
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md), [`useFieldArray`](/react/api/useFieldArray.md)
##### Methods
[`move`](/methods/api/move.md), [`remove`](/methods/api/remove.md), [`replace`](/methods/api/replace.md), [`swap`](/methods/api/swap.md)
### isDirty
Checks whether a specific field or the entire form is dirty by walking through the field store and all its descendants. A field is dirty when its input differs from its initial value.
```ts
const dirty = isDirty(form);
const dirty = isDirty(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `IsFormDirtyConfig | IsFieldDirtyConfig | undefined`
##### Explanation
The `form` parameter is the form store to check for dirtiness. The optional `config` parameter scopes the check to a specific field's subtree via a `path` (when omitted, the entire form is checked). The function walks through the field store and all its descendants and returns `true` as soon as it finds a dirty field.
#### Returns
- `result` `boolean`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`getDirtyInput`](/methods/api/getDirtyInput.md), [`getDirtyPaths`](/methods/api/getDirtyPaths.md), [`pickDirty`](/methods/api/pickDirty.md), [`isTouched`](/methods/api/isTouched.md), [`isEdited`](/methods/api/isEdited.md), [`isValid`](/methods/api/isValid.md)
### isEdited
Checks whether a specific field or the entire form is edited by walking through the field store and all its descendants. A field is edited once its value has been changed by the user.
```ts
const edited = isEdited(form);
const edited = isEdited(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `IsFormEditedConfig | IsFieldEditedConfig | undefined`
##### Explanation
The `form` parameter is the form store to check for being edited. The optional `config` parameter scopes the check to a specific field's subtree via a `path` (when omitted, the entire form is checked). The function walks through the field store and all its descendants and returns `true` as soon as it finds an edited field.
#### Returns
- `result` `boolean`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`isDirty`](/methods/api/isDirty.md), [`isTouched`](/methods/api/isTouched.md), [`isValid`](/methods/api/isValid.md)
### isTouched
Checks whether a specific field or the entire form is touched by walking through the field store and all its descendants. A field is touched once it has received focus or its value has been changed.
```ts
const touched = isTouched(form);
const touched = isTouched(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `IsFormTouchedConfig | IsFieldTouchedConfig | undefined`
##### Explanation
The `form` parameter is the form store to check for being touched. The optional `config` parameter scopes the check to a specific field's subtree via a `path` (when omitted, the entire form is checked). The function walks through the field store and all its descendants and returns `true` as soon as it finds a touched field.
#### Returns
- `result` `boolean`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`isDirty`](/methods/api/isDirty.md), [`isEdited`](/methods/api/isEdited.md), [`isValid`](/methods/api/isValid.md), [`focus`](/methods/api/focus.md)
### isValid
Checks whether a specific field or the entire form is valid by walking through the field store and all its descendants. A field is valid when neither it nor any of its descendants contains an error.
```ts
const valid = isValid(form);
const valid = isValid(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `IsFormValidConfig | IsFieldValidConfig | undefined`
##### Explanation
The `form` parameter is the form store to check for validity. The optional `config` parameter scopes the check to a specific field's subtree via a `path` (when omitted, the entire form is checked). The function walks through the field store and all its descendants and returns `false` as soon as it finds an error.
#### Returns
- `result` `boolean`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`getErrors`](/methods/api/getErrors.md), [`getDeepErrors`](/methods/api/getDeepErrors.md), [`isDirty`](/methods/api/isDirty.md), [`isTouched`](/methods/api/isTouched.md), [`isEdited`](/methods/api/isEdited.md)
### move
Moves an item within a field array from one index to another. All items between the old and new positions are shifted accordingly.
```ts
move(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldArrayPath` `extends RequiredPath`
#### Parameters
- `form` `BaseFormStore`
- `config` `MoveConfig`
##### Explanation
The `form` parameter is the form store containing the field array. The `config` parameter specifies the path to the field array and the indices to move the item from and to.
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md), [`useFieldArray`](/react/api/useFieldArray.md)
##### Methods
[`insert`](/methods/api/insert.md), [`remove`](/methods/api/remove.md), [`replace`](/methods/api/replace.md), [`swap`](/methods/api/swap.md)
### pickDirty
Picks only the dirty parts of the given value, using the form's dirty tree as a structural mask. Object keys whose subtree contains no dirty descendant are omitted; arrays are treated as atomic and returned in full whenever any descendant is dirty. Returns `undefined` if no field is dirty. Where the supplied value's shape diverges from the form's input shape — for example, a field expected to be an object holds a primitive, `null` or array — that branch is returned as-is. Useful for filtering a validated output to just the changed parts before submitting.
```ts
const dirty = pickDirty(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TValue` `extends object`
#### Parameters
- `form` `BaseFormStore`
- `config` `PickDirtyConfig`
##### Explanation
The `form` parameter is the form store providing the dirty mask. The `config` parameter must include a `from` value to filter. The value's shape is expected to match the form's input shape — wherever the shapes diverge (e.g. an object key in the form holds a primitive, `null` or array in the supplied value), that branch is returned as-is so the divergence is preserved in the result. Use this when a Valibot schema transformation produces a value of a different shape than the form's input and you want to ship only the dirty parts of that output.
Internally, the function walks the form's field tree alongside the supplied value, using `getFieldBool` to skip clean subtrees and a constant-time check at each node to verify shape alignment. Because the dirty check is itself recursive, the cost is effectively linear in field count for typical balanced forms (shallow and wide) and degrades toward `O(N²)` for deeply nested trees. Call this method from submit or blur handlers rather than from tight reactive loops on large forms.
#### Returns
- `result` `DeepPartial | undefined`
#### Related
##### Methods
[`getDirtyInput`](/methods/api/getDirtyInput.md), [`getDirtyPaths`](/methods/api/getDirtyPaths.md), [`getInput`](/methods/api/getInput.md)
### remove
Removes an item from a field array at the specified index. All items after the removed index are shifted down by one.
```ts
remove(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldArrayPath` `extends RequiredPath`
#### Parameters
- `form` `BaseFormStore`
- `config` `RemoveConfig`
##### Explanation
The `form` parameter is the form store containing the field array. The `config` parameter specifies the path to the field array and the index of the item to remove.
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md), [`useFieldArray`](/react/api/useFieldArray.md)
##### Methods
[`insert`](/methods/api/insert.md), [`move`](/methods/api/move.md), [`replace`](/methods/api/replace.md), [`swap`](/methods/api/swap.md)
### replace
Replaces an item in a field array at the specified index with a new item.
```ts
replace(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldArrayPath` `extends RequiredPath`
#### Parameters
- `form` `BaseFormStore`
- `config` `ReplaceConfig`
##### Explanation
The `form` parameter is the form store containing the field array. The `config` parameter specifies the path to the field array, the index of the item to replace, and the new item values.
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md), [`useFieldArray`](/react/api/useFieldArray.md)
##### Methods
[`insert`](/methods/api/insert.md), [`move`](/methods/api/move.md), [`remove`](/methods/api/remove.md), [`swap`](/methods/api/swap.md)
### reset
Resets the form or a specific field to its initial state or to a new initial input. Optionally keeps certain states like input values, touched state, or errors.
```ts
reset(form);
reset(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `ResetFormConfig | ResetFieldConfig | undefined`
##### Explanation
When called without a config or with `path` set to `undefined`, the entire form is reset. When a `path` is provided, only that specific field and its nested fields are reset.
By default, the form resets to its original initial input. However, you can pass a new `initialInput` in the config to update the form's baseline data. This is useful when remote data has changed and the form needs to reflect the updated values. Combined with `keepInput`, `keepTouched`, `keepEdited`, `keepErrors`, and `keepSubmitted`, you can update the initial state without overwriting the user's current edits.
Resetting a field discards pending validation of the previous form input, including results for other fields. Existing errors on unrelated fields are preserved. Call [`validate`](/methods/api/validate.md) if you need fresh results immediately after a field reset.
A full form reset clears `isSubmitting`. Pending submissions no longer call their handlers after validation finishes, and late exceptions from their validations or handlers do not update form errors. Resetting does not abort a handler or network request that has already started.
###### More context on dirty tracking
Formisch distinguishes between two concepts:
- **Initial input**: the baseline value used for dirty tracking (often the server state).
- **Current input**: the value the user is currently editing (the client state).
Calling `reset` updates the initial input (baseline). Depending on the `keep*` options, it can also overwrite the current input, or keep it and just recompute state like `isDirty` based on the new baseline.
> If the initial input is `null` or `undefined`, Formisch does not treat an empty string or `NaN` as dirty. This helps when HTML inputs produce `''` or `NaN` for "empty" values.
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`setInput`](/methods/api/setInput.md), [`getInput`](/methods/api/getInput.md), [`setErrors`](/methods/api/setErrors.md), [`validate`](/methods/api/validate.md)
### setErrors
Sets or clears error messages on the form or a specific field. This is useful for setting custom validation errors that don't come from schema validation.
```ts
setErrors(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `SetFormErrorsConfig | SetFieldErrorsConfig`
##### Explanation
The `form` parameter is the form store to set errors on. The `config` parameter specifies the path and the error messages to set. Set `errors` to `null` to clear existing errors.
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`getErrors`](/methods/api/getErrors.md), [`getDeepErrors`](/methods/api/getDeepErrors.md), [`validate`](/methods/api/validate.md)
### setInput
Sets input values for the form or a specific field programmatically.
```ts
setInput(form, config);
setInput(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath | undefined = undefined`
#### Parameters
- `form` `BaseFormStore`
- `config` `SetFormInputConfig | SetFieldInputConfig`
##### Explanation
The `form` parameter is the form store to set input on. The `config` parameter specifies the path to the field and the new input values to set.
> `setInput` updates the **current input** (what the user is editing), but it does not change the **initial input** (the baseline used for `isDirty`). If your baseline data changes (e.g. after refetching from the server), prefer [`reset`](/methods/api/reset.md) with a new `initialInput`.
Updating input discards pending validation results for the previous input. A new validation only starts when required by the form's validation mode.
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`getInput`](/methods/api/getInput.md), [`reset`](/methods/api/reset.md)
### submit
Programmatically requests form submission by calling the native `requestSubmit()` method on the underlying form element.
```ts
submit(form);
```
#### Parameters
- `form` `BaseFormStore`
##### Explanation
The `form` parameter is the form store to submit. The function triggers form submission by calling the native `requestSubmit()` method on the underlying form element.
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`handleSubmit`](/methods/api/handleSubmit.md), [`validate`](/methods/api/validate.md), [`focus`](/methods/api/focus.md)
### swap
Swaps two items in a field array at the specified indices.
```ts
swap(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldArrayPath` `extends RequiredPath`
#### Parameters
- `form` `BaseFormStore`
- `config` `SwapConfig`
##### Explanation
The `form` parameter is the form store containing the field array. The `config` parameter specifies the path to the field array and the two indices of the items to swap.
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md), [`useFieldArray`](/react/api/useFieldArray.md)
##### Methods
[`insert`](/methods/api/insert.md), [`move`](/methods/api/move.md), [`remove`](/methods/api/remove.md), [`replace`](/methods/api/replace.md)
### validate
Validates the entire form input against its schema. Returns a safe parse result indicating success or failure with detailed issues. Optionally focuses the first field with validation errors.
```ts
const result = validate(form);
const result = validate(form, config);
```
#### Generics
- `TSchema` `extends FormSchema`
#### Parameters
- `form` `BaseFormStore`
- `config` `ValidateFormConfig | undefined`
##### Explanation
The `form` parameter is the form store to validate. The optional `config` parameter allows specifying whether to automatically focus the first field with validation errors after validation.
#### Returns
- `result` `Promise>`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`focus`](/methods/api/focus.md), [`handleSubmit`](/methods/api/handleSubmit.md), [`setErrors`](/methods/api/setErrors.md), [`getErrors`](/methods/api/getErrors.md), [`getDeepErrors`](/methods/api/getDeepErrors.md)
## Types (api)
### DeepErrorEntry
Entry that pairs the `path` to a field with its error messages. Returned by the `getDeepErrorEntries` and `getDeepErrorEntry` methods. Form-level errors are included with an empty path.
#### Generics
- `TValue` `extends unknown = unknown`
#### Definition
- `DeepErrorEntry`
- `path` `unknown extends TValue ? Path : readonly [] | FieldPath`
- `errors` `[string, ...string[]]`
#### Related
##### Methods
[`getDeepErrorEntries`](/methods/api/getDeepErrorEntries.md), [`getDeepErrorEntry`](/methods/api/getDeepErrorEntry.md)
### DeepPartial
A utility type that recursively makes all properties and nested properties optional.
#### Generics
- `TValue` `extends unknown`
> This type is too complex to display. Please refer to the [source code](https://github.com/open-circle/formisch/blob/main/packages/core/src/types/utils/utils.ts).
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
### EmptyInput
Empty input interface that defines the value a required field of a given type starts at when no initial input is provided. Optional and nullable fields are not affected, as they accept `undefined`.
#### Definition
- `EmptyInput`
- `string` `string | undefined`
- `number` `number | undefined`
- `boolean` `boolean | undefined`
- `date` `Date | undefined`
##### Explanation
Only required fields whose input is `undefined` fall back to these values. The `string` property defaults to an empty string so that an untouched field matches the DOM and validates with its own message instead of a type mismatch. The other properties default to `undefined`.
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Types
[`FormConfig`](/react/api/FormConfig.md)
### FieldArrayStore
Reactive field array store interface with properties for managing array fields.
#### Generics
- `TSchema` `extends FormSchema = FormSchema`
- `TFieldArrayPath` `extends RequiredPath = RequiredPath`
#### Definition
- `FieldArrayStore`
- `path` `ValidArrayPath, TFieldArrayPath>`
- `items` `string[]`
- `errors` `[string, ...string[]] | null`
- `isTouched` `boolean`
- `isEdited` `boolean`
- `isDirty` `boolean`
- `isValid` `boolean`
### FieldElement
A union type representing form field elements that can be registered with a field store.
#### Definition
- `FieldElement` `HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement`
#### Related
##### Methods
[`focus`](/methods/api/focus.md)
### FieldElementProps
Props to spread onto a field element for integration with HonoX form handling.
#### Definition
- `FieldElementProps`
- `name` `string`
- `autofocus` `boolean`
- `ref` `((element: FieldElement | null) => (() => void) | undefined)`
- `onFocus` `() => void`
- `onChange` `(event: Event) => void`
- `onBlur` `() => void`
##### Explanation
`onFocus` marks the field as touched and runs the configured `'touch'` validation, while `onBlur` runs the configured `'blur'` validation. Neither handler uses the event object, so both are typed as `() => void` and can be passed to native elements or custom components with different focus event types.
The `ref` callback registers the field element, which enables methods like [`focus`](/methods/api/focus.md) to control it. It returns a cleanup function that hono/jsx calls when it removes the element, which is how the field unregisters it.
Since hono/jsx types its event handlers with native DOM events, `onChange` receives a native `Event`. In addition, hono/jsx maps `onChange` to the native `input` event, so the field input is updated on every keystroke and not only when the element loses focus.
### FieldStore
Reactive field store interface with properties and element props for field management.
#### Generics
- `TSchema` `extends FormSchema = FormSchema`
- `TFieldPath` `extends RequiredPath = RequiredPath`
#### Definition
- `FieldStore`
- `path` `ValidPath, TFieldPath>`
- `input` `PartialValues, TFieldPath>>`
- `errors` `[string, ...string[]] | null`
- `isTouched` `boolean`
- `isEdited` `boolean`
- `isDirty` `boolean`
- `isValid` `boolean`
- `onChange` `(value: PartialValues, TFieldPath>>) => void`
- `props` `FieldElementProps`
### FocusFieldConfig
Configuration interface for focusing a field. Used by the `focus` method to specify which field to focus.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `FocusFieldConfig`
- `path` `ValidPath, TFieldPath>`
#### Related
##### Methods
[`focus`](/methods/api/focus.md)
### FormConfig
Form config interface that defines the configuration options for creating a form store with [`useForm`](/honox/api/useForm.md).
#### Generics
- `TSchema` `extends FormSchema = FormSchema`
#### Definition
- `FormConfig`
- `schema` `TSchema`
- `initialInput` `DeepPartial> | undefined`
- `emptyInput` `EmptyInput | undefined`
- `validate` `ValidationMode | undefined`
- `revalidate` `Exclude | undefined`
##### Explanation
The `FormConfig` object is passed to [`useForm`](/honox/api/useForm.md) with a required `schema` property and optional properties for `initialInput`, `emptyInput`, `validate`, and `revalidate` to control form behavior and validation timing.
### FormSchema
Type definition that represents the Valibot schemas allowed as the root of a form. Forms must have an object root, so this is constrained structurally to any schema with an object output rather than to specific schema types. This includes object schemas, combinators (`intersect`, `union`, `variant`) over objects, `lazy` schemas wrapping any of these, and generic object schemas like `v.GenericSchema<{ … }>`. Use [Schema](/core/api/Schema.md) for nested field schemas.
#### Definition
- `FormSchema` `GenericSchema> | GenericSchemaAsync>`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`validate`](/methods/api/validate.md)
### FormStore
Form store interface that provides reactive access to the state of a form created with [`useForm`](/honox/api/useForm.md).
#### Generics
- `TSchema` `extends FormSchema = FormSchema`
#### Definition
- `FormStore`
- `isSubmitting` `boolean`
- `isSubmitted` `boolean`
- `isValidating` `boolean`
- `isTouched` `boolean`
- `isEdited` `boolean`
- `isDirty` `boolean`
- `isValid` `boolean`
- `errors` `[string, ... string[]] | null`
##### Explanation
The `errors` property only contains validation errors at the root level of the form. These are errors from the root object schema validation itself, not errors from nested fields. To retrieve all validation errors from all fields across the entire form, use the [`getDeepErrors`](/methods/api/getDeepErrors.md) method instead.
### GetFieldDeepErrorConfig
Configuration interface for retrieving the first error of a specific field. Used by the `getDeepError` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `GetFieldDeepErrorConfig`
- `path` `ValidPath, TFieldPath>`
#### Related
##### Methods
[`getDeepError`](/methods/api/getDeepError.md)
### GetFieldDeepErrorEntriesConfig
Configuration interface for retrieving the error entries of a specific field. Used by the `getDeepErrorEntries` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `GetFieldDeepErrorEntriesConfig`
- `path` `ValidPath, TFieldPath>`
#### Related
##### Methods
[`getDeepErrorEntries`](/methods/api/getDeepErrorEntries.md)
### GetFieldDeepErrorEntryConfig
Configuration interface for retrieving the first error entry of a specific field. Used by the `getDeepErrorEntry` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `GetFieldDeepErrorEntryConfig`
- `path` `ValidPath, TFieldPath>`
#### Related
##### Methods
[`getDeepErrorEntry`](/methods/api/getDeepErrorEntry.md)
### GetFieldDeepErrorsConfig
Configuration interface for retrieving the errors of a specific field. Used by the `getDeepErrors` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `GetFieldDeepErrorsConfig`
- `path` `ValidPath, TFieldPath>`
#### Related
##### Methods
[`getDeepErrors`](/methods/api/getDeepErrors.md)
### GetFieldDirtyInputConfig
Configuration interface for retrieving field-scoped dirty input. Used by the `getDirtyInput` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `GetFieldDirtyInputConfig`
- `path` `ValidPath, TFieldPath>`
#### Related
##### Methods
[`getDirtyInput`](/methods/api/getDirtyInput.md)
### GetFieldDirtyPathsConfig
Configuration interface for inspecting field-scoped dirty paths. Used by the `getDirtyPaths` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `GetFieldDirtyPathsConfig`
- `path` `ValidPath, TFieldPath>`
#### Related
##### Methods
[`getDirtyPaths`](/methods/api/getDirtyPaths.md)
### GetFieldErrorsConfig
Configuration interface for retrieving field-specific errors. Used by the `getErrors` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `GetFieldErrorsConfig`
- `path` `ValidPath, TFieldPath>`
#### Related
##### Methods
[`getErrors`](/methods/api/getErrors.md)
### GetFieldInputConfig
Configuration interface for retrieving field-specific input. Used by the `getInput` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `GetFieldInputConfig`
- `path` `ValidPath, TFieldPath>`
#### Related
##### Methods
[`getInput`](/methods/api/getInput.md)
### GetFormDeepErrorConfig
Configuration interface for retrieving the first error of the entire form. Used by the `getDeepError` method when no specific field path is provided.
#### Definition
- `GetFormDeepErrorConfig`
- `path` `undefined = undefined`
#### Related
##### Methods
[`getDeepError`](/methods/api/getDeepError.md)
### GetFormDeepErrorEntriesConfig
Configuration interface for retrieving the error entries of the entire form. Used by the `getDeepErrorEntries` method when no specific field path is provided.
#### Definition
- `GetFormDeepErrorEntriesConfig`
- `path` `undefined = undefined`
#### Related
##### Methods
[`getDeepErrorEntries`](/methods/api/getDeepErrorEntries.md)
### GetFormDeepErrorEntryConfig
Configuration interface for retrieving the first error entry of the entire form. Used by the `getDeepErrorEntry` method when no specific field path is provided.
#### Definition
- `GetFormDeepErrorEntryConfig`
- `path` `undefined = undefined`
#### Related
##### Methods
[`getDeepErrorEntry`](/methods/api/getDeepErrorEntry.md)
### GetFormDeepErrorsConfig
Configuration interface for retrieving the errors of the entire form. Used by the `getDeepErrors` method when no specific field path is provided.
#### Definition
- `GetFormDeepErrorsConfig`
- `path` `undefined = undefined`
#### Related
##### Methods
[`getDeepErrors`](/methods/api/getDeepErrors.md)
### GetFormDirtyInputConfig
Configuration interface for retrieving form-level dirty input. Used by the `getDirtyInput` method when no specific field path is provided.
#### Definition
- `GetFormDirtyInputConfig`
- `path` `undefined = undefined`
#### Related
##### Methods
[`getDirtyInput`](/methods/api/getDirtyInput.md)
### GetFormDirtyPathsConfig
Configuration interface for inspecting form-level dirty paths. Used by the `getDirtyPaths` method when no specific field path is provided.
#### Definition
- `GetFormDirtyPathsConfig`
- `path` `undefined = undefined`
#### Related
##### Methods
[`getDirtyPaths`](/methods/api/getDirtyPaths.md)
### GetFormErrorsConfig
Configuration interface for retrieving form-level errors. Used by the `getErrors` method when no specific field path is provided.
#### Definition
- `GetFormErrorsConfig`
- `path` `undefined = undefined`
#### Related
##### Methods
[`getErrors`](/methods/api/getErrors.md)
### GetFormInputConfig
Configuration interface for retrieving form-level input. Used by the `getInput` method when no specific field path is provided.
#### Definition
- `GetFormInputConfig`
- `path` `undefined = undefined`
#### Related
##### Methods
[`getInput`](/methods/api/getInput.md)
### InsertConfig
Configuration interface for inserting items into array fields. Used by the `insert` method.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldArrayPath` `extends RequiredPath`
#### Definition
- `InsertConfig`
- `path` `ValidArrayPath, TFieldArrayPath>`
- `at` `number | undefined`
- `initialInput` `DeepPartial, [...TFieldArrayPath, number]>> | undefined`
#### Related
##### Methods
[`insert`](/methods/api/insert.md)
### IsFieldDirtyConfig
Configuration interface for checking whether a specific field is dirty. Used by the `isDirty` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `IsFieldDirtyConfig`
- `path` `ValidPath, TFieldPath>`
#### Related
##### Methods
[`isDirty`](/methods/api/isDirty.md)
### IsFieldEditedConfig
Configuration interface for checking whether a specific field is edited. Used by the `isEdited` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `IsFieldEditedConfig`
- `path` `ValidPath, TFieldPath>`
#### Related
##### Methods
[`isEdited`](/methods/api/isEdited.md)
### IsFieldTouchedConfig
Configuration interface for checking whether a specific field is touched. Used by the `isTouched` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `IsFieldTouchedConfig`
- `path` `ValidPath, TFieldPath>`
#### Related
##### Methods
[`isTouched`](/methods/api/isTouched.md)
### IsFieldValidConfig
Configuration interface for checking whether a specific field is valid. Used by the `isValid` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `IsFieldValidConfig`
- `path` `ValidPath, TFieldPath>`
#### Related
##### Methods
[`isValid`](/methods/api/isValid.md)
### IsFormDirtyConfig
Configuration interface for checking whether the entire form is dirty. Used by the `isDirty` method when no specific field path is provided.
#### Definition
- `IsFormDirtyConfig`
- `path` `undefined = undefined`
#### Related
##### Methods
[`isDirty`](/methods/api/isDirty.md)
### IsFormEditedConfig
Configuration interface for checking whether the entire form is edited. Used by the `isEdited` method when no specific field path is provided.
#### Definition
- `IsFormEditedConfig`
- `path` `undefined = undefined`
#### Related
##### Methods
[`isEdited`](/methods/api/isEdited.md)
### IsFormTouchedConfig
Configuration interface for checking whether the entire form is touched. Used by the `isTouched` method when no specific field path is provided.
#### Definition
- `IsFormTouchedConfig`
- `path` `undefined = undefined`
#### Related
##### Methods
[`isTouched`](/methods/api/isTouched.md)
### IsFormValidConfig
Configuration interface for checking whether the entire form is valid. Used by the `isValid` method when no specific field path is provided.
#### Definition
- `IsFormValidConfig`
- `path` `undefined = undefined`
#### Related
##### Methods
[`isValid`](/methods/api/isValid.md)
### MoveConfig
Configuration interface for moving items within array fields. Used by the `move` method.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldArrayPath` `extends RequiredPath`
#### Definition
- `MoveConfig`
- `path` `ValidArrayPath, TFieldArrayPath>`
- `from` `number`
- `to` `number`
#### Related
##### Methods
[`move`](/methods/api/move.md)
### PartialValues
Utility type that recursively makes all value properties optional, including nested objects and arrays.
#### Generics
- `TValue` `extends unknown`
#### Definition
> This type is too complex to display. Please refer to the [source code](https://github.com/open-circle/formisch/blob/main/packages/core/src/types/utils/utils.ts).
#### Related
##### Methods
[`getInput`](/methods/api/getInput.md)
### Path
Type definition for readonly arrays of path keys used to access nested properties.
#### Definition
- `Path` `ReadonlyArray`
### PathKey
Type definition for keys used in paths to access nested properties in objects and arrays.
#### Definition
- `PathKey` `string | number`
### PathValue
Conditional type that extracts the value type at the given path.
#### Generics
- `TValue` `extends unknown`
- `TPath` `extends Path`
#### Definition
- `PathValue` `PathValue`
#### Related
##### Methods
[`getInput`](/methods/api/getInput.md), [`insert`](/methods/api/insert.md), [`replace`](/methods/api/replace.md), [`reset`](/methods/api/reset.md), [`setInput`](/methods/api/setInput.md)
### PickDirtyConfig
Configuration interface for picking dirty parts of a value. Used by the `pickDirty` method.
#### Generics
- `TValue` `extends object`
#### Definition
- `PickDirtyConfig`
- `from` `TValue`
#### Related
##### Methods
[`pickDirty`](/methods/api/pickDirty.md)
### RemoveConfig
Configuration interface for removing items from array fields. Used by the `remove` method.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldArrayPath` `extends RequiredPath`
#### Definition
- `RemoveConfig`
- `path` `ValidArrayPath, TFieldArrayPath>`
- `at` `number`
#### Related
##### Methods
[`remove`](/methods/api/remove.md)
### ReplaceConfig
Configuration interface for replacing items in array fields. Used by the `replace` method.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldArrayPath` `extends RequiredPath`
#### Definition
- `ReplaceConfig`
- `path` `ValidArrayPath, TFieldArrayPath>`
- `at` `number`
- `initialInput` `DeepPartial, [...TFieldArrayPath, number]>> | undefined`
#### Related
##### Methods
[`replace`](/methods/api/replace.md)
### RequiredPath
A type that represents a required path into an object structure with at least one key.
#### Definition
- `RequiredPath` `readonly [PathKey, ...Path]`
#### Related
##### Methods
[`focus`](/methods/api/focus.md), [`getErrors`](/methods/api/getErrors.md), [`getInput`](/methods/api/getInput.md), [`replace`](/methods/api/replace.md), [`setInput`](/methods/api/setInput.md), [`swap`](/methods/api/swap.md)
### ResetFieldConfig
Configuration interface for resetting field-specific input and errors. Used by the `reset` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `ResetFieldConfig`
- `path` `ValidPath, TFieldPath>`
- `initialInput` `DeepPartial, TFieldPath>> = undefined`
- `keepEdited` `boolean = false`
- `keepErrors` `boolean = false`
- `keepInput` `boolean = false`
- `keepTouched` `boolean = false`
#### Related
##### Methods
[`reset`](/methods/api/reset.md)
### ResetFormConfig
Configuration interface for resetting form input and errors. Used by the `reset` method when no specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
#### Definition
- `ResetFormConfig`
- `path` `undefined = undefined`
- `initialInput` `DeepPartial> = undefined`
- `keepEdited` `boolean = false`
- `keepErrors` `boolean = false`
- `keepInput` `boolean = false`
- `keepSubmitted` `boolean = false`
- `keepTouched` `boolean = false`
#### Related
##### Methods
[`reset`](/methods/api/reset.md)
### Schema
Schema type definition that represents any Valibot schema that can be used with Formisch.
#### Definition
- `Schema` `GenericSchema | GenericSchemaAsync`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`validate`](/methods/api/validate.md)
### SetFieldErrorsConfig
Configuration interface for setting field-specific errors. Used by the `setErrors` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `SetFieldErrorsConfig`
- `path` `ValidPath, TFieldPath>`
- `errors` `[string, ...string[]] | null`
#### Related
##### Methods
[`setErrors`](/methods/api/setErrors.md)
### SetFieldInputConfig
Configuration interface for setting field-specific input. Used by the `setInput` method when a specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldPath` `extends RequiredPath`
#### Definition
- `SetFieldInputConfig`
- `path` `ValidPath, TFieldPath>`
- `input` `PathValue, TFieldPath>`
#### Related
##### Methods
[`setInput`](/methods/api/setInput.md)
### SetFormErrorsConfig
Configuration interface for setting form-level errors. Used by the `setErrors` method when no specific field path is provided.
#### Definition
- `SetFormErrorsConfig`
- `path` `undefined = undefined`
- `errors` `[string, ...string[]] | null`
#### Related
##### Methods
[`setErrors`](/methods/api/setErrors.md)
### SetFormInputConfig
Configuration interface for setting form-level input. Used by the `setInput` method when no specific field path is provided.
#### Generics
- `TSchema` `extends FormSchema`
#### Definition
- `SetFormInputConfig`
- `path` `undefined = undefined`
- `input` `v.InferInput`
#### Related
##### Methods
[`setInput`](/methods/api/setInput.md)
### SubmitEventHandler
Function type for handling form submission with the submit event. Used by the `handleSubmit` method and form components.
#### Generics
- `TSchema` `extends FormSchema`
#### Parameters
- `output` `InferOutput`
- `event` `SubmitEvent`
#### Related
##### Methods
[`handleSubmit`](/methods/api/handleSubmit.md)
##### Types
[`SubmitHandler`](/core/api/SubmitHandler.md)
### SubmitHandler
Function type for handling form submission without the submit event. Used by the `handleSubmit` method.
#### Generics
- `TSchema` `extends FormSchema`
#### Parameters
- `output` `InferOutput`
#### Related
##### Methods
[`handleSubmit`](/methods/api/handleSubmit.md)
##### Types
[`SubmitEventHandler`](/core/api/SubmitEventHandler.md)
### SwapConfig
Configuration interface for swapping items in array fields. Used by the `swap` method.
#### Generics
- `TSchema` `extends FormSchema`
- `TFieldArrayPath` `extends RequiredPath`
#### Definition
- `SwapConfig`
- `path` `ValidArrayPath, TFieldArrayPath>`
- `at` `number`
- `and` `number`
#### Related
##### Methods
[`swap`](/methods/api/swap.md)
### UseFieldArrayConfig
Configuration object for creating a field array store with [`useFieldArray`](/honox/api/useFieldArray.md).
#### Generics
- `TSchema` `extends FormSchema = FormSchema`
- `TFieldArrayPath` `extends RequiredPath = RequiredPath`
#### Definition
- `UseFieldArrayConfig`
- `path` `ValidArrayPath, TFieldArrayPath>`
### UseFieldConfig
Configuration object for creating a field store with [`useField`](/honox/api/useField.md).
#### Generics
- `TSchema` `extends FormSchema = FormSchema`
- `TFieldPath` `extends RequiredPath = RequiredPath`
#### Definition
- `UseFieldConfig`
- `path` `ValidPath, TFieldPath>`
### ValidArrayPath
A utility type that validates array paths and returns the first possible valid array path based on the given value if the provided path is invalid.
#### Generics
- `TValue` `extends unknown`
- `TPath` `extends RequiredPath`
> This type is too complex to display. Please refer to the [source code](https://github.com/open-circle/formisch/blob/main/packages/core/src/types/path/path.ts).
### ValidateFormConfig
Configuration interface for form validation. Used by the `validate` method to specify validation behavior.
#### Definition
- `ValidateFormConfig`
- `shouldFocus` `boolean = false`
#### Related
##### Methods
[`validate`](/methods/api/validate.md)
### ValidationMode
Validation mode type that defines when and how form validation is triggered.
#### Definition
- `ValidationMode` `'initial' | 'touch' | 'input' | 'change' | 'blur' | 'submit'`
#### Related
##### Primitives
[`useForm`](/react/api/useForm.md)
##### Methods
[`validate`](/methods/api/validate.md)
### ValidPath
Conditional type that returns the path if valid, otherwise the first possible valid path based on the given value.
#### Generics
- `TValue` `extends unknown`
- `TPath` `extends RequiredPath`
#### Definition
- `ValidPath` `TPath extends LazyPath, TPath> ? TPath : LazyPath, TPath>`
#### Related
##### Methods
[`focus`](/methods/api/focus.md), [`getErrors`](/methods/api/getErrors.md), [`getInput`](/methods/api/getInput.md), [`reset`](/methods/api/reset.md), [`setErrors`](/methods/api/setErrors.md), [`setInput`](/methods/api/setInput.md)