Formisch
Form handling with Formisch, the schema-first and type-safe form library for Angular, Preact, Qwik, React, React Native, Solid, Svelte, and Vue. Use when creating forms, handling form state, validating inputs, working with field arrays, or using @formisch/* packages.
- Skill ID
- open-circle/agent-skills/formisch
- Publisher
- open-circle
- Repository
- agent-skills
- Installs
- 0
- Files
- 2
- License
- MIT
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
open-circle/agent-skills/formischInstalls these files- .claude-plugin/plugin.json
- SKILL.md
What this skill tells the agent
Formisch
This skill helps AI agents work effectively with Formisch, the schema-based, headless form library for modern frameworks.
When to Use This Skill
- When the user asks about form handling with Formisch
- When managing form state and validation
- When working with Angular, Preact, Qwik, React, React Native, Solid, Svelte, or Vue forms
- When integrating Valibot schemas with forms
Introduction
Formisch is a schema-based, headless form library that works across multiple frameworks. Key highlights:
- Small bundle size — Starting at ~2.5 kB
- Schema-based validation — Uses Valibot for type-safe validation
- Headless design — You control the UI completely
- Type safety — Full TypeScript support with autocompletion
- Framework-native — Native performance for each supported framework
Supported Frameworks
| Framework | Package | Hook/Primitive |
|---|---|---|
| Angular | @formisch/angular | injectForm |
| Preact | @formisch/preact | useForm |
| Qwik | @formisch/qwik | useForm$ |
| React | @formisch/react | useForm |
| React Native | @formisch/react-native | useForm |
| SolidJS | @formisch/solid | createForm |
| Svelte | @formisch/svelte | createForm |
| Vue | @formisch/vue | useForm |
Installation
1. Install Valibot (peer dependency)
npm install valibot2. Install Formisch for your framework
npm install @formisch/react # React
npm install @formisch/angular # Angular
npm install @formisch/vue # Vue
npm install @formisch/solid # SolidJS
npm install @formisch/preact # Preact
npm install @formisch/svelte # Svelte
npm install @formisch/qwik # Qwik
npm install @formisch/react-native # React NativeCore Concepts
Schema-First Design
Every form starts with a Valibot schema. Types are automatically inferred from the schema.
import * as v from "valibot";
const LoginSchema = v.object({
email: v.pipe(
v.string("Please enter your email."),
v.nonEmpty("Please enter your email."),
v.email("The email address is badly formatted."),
),
password: v.pipe(
v.string("Please enter your password."),
v.nonEmpty("Please enter your password."),
v.minLength(8, "Your password must have 8 characters or more."),
),
});Form Store
The form store manages all form state. Access it via the framework-specific hook/primitive.
Form Store Properties:
isSubmitting— Form is currently being submittedisSubmitted— Form submission has been attemptedisValidating— Validation is in progressisTouched— At least one field has been touchedisEdited— At least one field has been editedisDirty— At least one field differs from initial valueisValid— All fields pass validationerrors— Root-level validation errors
Field Store
Each field has its own reactive store with:
path— Path array to the fieldinput— Current field valueerrors— Field-specific errorsisTouched— Field has been focusedisEdited— Field value has been editedisDirty— Field value differs from initial valueisValid— Field passes validationprops— Props to spread onto native elements (Angular connects controls with[formischControl]instead)onChange(React and React Native) /onInput(Solid, Svelte, Preact, and Qwik) /setInput(Angular) — Sets the field input value programmatically. Use this when the field cannot be connected to a native element. In Vue, setfield.inputdirectly (for example withv-model).
Store reactivity is framework-specific. React, React Native, Solid, Svelte, and Vue expose plain reactive properties. Angular properties are signals and are called like field.errors(), except path, which is a plain value. Preact and Qwik properties are signals; use .value in conditions and ordinary TypeScript logic. Do not copy one framework's access syntax into another.
Dirty Tracking
Formisch tracks two inputs per field:
- Initial input — Baseline for dirty tracking (server state)
- Current input — What the user is editing (client state)
isDirty becomes true when current input differs from initial input.
Framework Examples
Angular Example
Angular uses signals, dependency injection, and directives instead of a JSX component API.
import { Component } from "@angular/core";
import {
FormischControl,
FormischField,
FormischForm,
injectForm,
type SubmitHandler,
} from "@formisch/angular";
import * as v from "valibot";
const LoginSchema = v.object({
email: v.pipe(v.string(), v.email()),
password: v.pipe(v.string(), v.minLength(8)),
});
@Component({
selector: "app-login",
imports: [FormischForm, FormischField, FormischControl],
template: `
<form [formischForm]="loginForm" [formischSubmit]="handleSubmit">
<ng-container *formischField="['email'] of loginForm; let field">
<input [formischControl]="field" type="email" />
@if (field.errors(); as errors) {
<div>{{ errors[0] }}</div>
}
</ng-container>
<button type="submit" [disabled]="loginForm.isSubmitting()">Login</button>
</form>
`,
})
export class LoginComponent {
readonly loginForm = injectForm({ schema: LoginSchema });
readonly handleSubmit: SubmitHandler<typeof LoginSchema> = (output) => {
console.log(output);
};
}Let [formischControl] synchronize the native control. Do not add competing [value] or [checked] bindings except when value identifies an option in a radio or checkbox group.
React Native Example
React Native has no DOM <form> element or Formisch Form component. Use handleSubmit and bind field.props to TextInput.
import { Field, handleSubmit, useForm } from "@formisch/react-native";
import { Button, TextInput, View } from "react-native";
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 LoginScreen() {
const loginForm = useForm({ schema: LoginSchema });
const submitForm = handleSubmit(loginForm, (output) => console.log(output));
return (
<View>
<Field of={loginForm} path={["email"]}>
{(field) => (
<TextInput
{...field.props}
value={field.input}
autoCapitalize="none"
keyboardType="email-address"
/>
)}
</Field>
<Field of={loginForm} path={["password"]}>
{(field) => (
<TextInput {...field.props} value={field.input} secureTextEntry />
)}
</Field>
<Button title="Login" onPress={submitForm} />
</View>
);
}React Native text inputs are controlled, so always pass value={field.input}. Use field.onChange(value) for switches, sliders, pickers, and non-string values.
React Example
import { Field, Form, useForm } from "@formisch/react";
import type { SubmitHandler } from "@formisch/react";
import * as v from "valibot";
const LoginSchema = v.object({
email: v.pipe(v.string(), v.email()),
password: v.pipe(v.string(), v.minLength(8)),
});
export default function LoginPage() {
const loginForm = useForm({
schema: LoginSchema,
});
const handleSubmit: SubmitHandler<typeof LoginSchema> = (output) => {
console.log(output); // { email: string, password: string }
};
return (
<Form of={loginForm} onSubmit={handleSubmit}>
<Field of={loginForm} path={["email"]}>
{(field) => (
<div>
<input {...field.props} value={field.input} type="email" />
{field.errors && <div>{field.errors[0]}</div>}
</div>
)}More from open-circle
Use this skill in a project
Open any RiverX project, search the Skills panel for open-circle/agent-skills/formisch, and install it in one click.
