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.
来源信息
- 仓库
- open-circle/agent-skills
- 最近来源活动
- 2026年8月21日 21:16
- 检测到的 SKILL.md 语言
- 英语
- 星标
- 16
- 分支
- 1
安装方式
默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。
检查来源文件
决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。
文件资源管理器
2 个文件正在显示 SKILL.md
SKILL.md
来源说明 · 只读预览- name
- formisch
- description
- 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.
- license
- MIT
- metadata
- {"author":"open-circle","version":"1.3"}
# Formisch
This skill helps AI agents work effectively with [Formisch](https://formisch.dev/), 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)
```bash
npm install valibot
```
### 2. Install Formisch for your framework
```bash
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 Native
```
## Core Concepts
### Schema-First Design
Every form starts with a Valibot schema. Types are automatically inferred from the schema.
```ts
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 submitted
- `isSubmitted` — Form submission has been attempted
- `isValidating` — Validation is in progress
- `isTouched` — At least one field has been touched
- `isEdited` — At least one field has been edited
- `isDirty` — At least one field differs from initial value
- `isValid` — All fields pass validation
- `errors` — Root-level validation errors
### Field Store
Each field has its own reactive store with:
- `path` — Path array to the field
- `input` — Current field value
- `errors` — Field-specific errors
- `isTouched` — Field has been focused
- `isEdited` — Field value has been edited
- `isDirty` — Field value differs from initial value
- `isValid` — Field passes validation
- `props` — 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, set `field.input` directly (for example with `v-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.
```ts
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`.
```tsx
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
```tsx
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>
)}
</Field>
<Field of={loginForm} path={["password"]}>
{(field) => (
<div>
<input {...field.props} value={field.input} type="password" />
{field.errors && <div>{field.errors[0]}</div>}
</div>
)}
</Field>
<button type="submit" disabled={loginForm.isSubmitting}>
{loginForm.isSubmitting ? "Submitting..." : "Login"}
</button>
</Form>
);
}
```
### Vue Example
```vue
<script setup lang="ts">
import { Field, Form, useForm } from "@formisch/vue";
import type { SubmitHandler } from "@formisch/vue";
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 loginForm = useForm({
schema: LoginSchema,
});
const handleSubmit: SubmitHandler<typeof LoginSchema> = (output) => {
console.log(output);
};
</script>
<template>
<Form :of="loginForm" @submit="handleSubmit">
<Field :of="loginForm" :path="['email']" v-slot="field">
<div>
<input v-model="field.input" v-bind="field.props" type="email" />
<div v-if="field.errors">{{ field.errors[0] }}</div>
</div>
</Field>
<Field :of="loginForm" :path="['password']" v-slot="field">
<div>
<input v-model="field.input" v-bind="field.props" type="password" />
<div v-if="field.errors">{{ field.errors[0] }}</div>
</div>
</Field>
<button type="submit">Login</button>
</Form>
</template>
```
### SolidJS Example
```tsx
import { Field, Form, createForm } from "@formisch/solid";
import type { SubmitHandler } from "@formisch/solid";
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 = createForm({
schema: LoginSchema,
});
const handleSubmit: SubmitHandler<typeof LoginSchema> = (output) => {
console.log(output);
};
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>
)}
</Field>
<Field of={loginForm} path={["password"]}>
{(field) => (
<div>
<input {...field.props} value={field.input} type="password" />
{field.errors && <div>{field.errors[0]}</div>}
</div>
)}
</Field>
<button type="submit">Login</button>
</Form>
);
}
```
### Svelte Example
```svelte
<script lang="ts">
import { createForm, Field, Form } from '@formisch/svelte';
import type { SubmitHandler } from '@formisch/svelte';
import * as v from 'valibot';
在 GitHub 查看这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看