Introduction

Formisch is a schema-based, headless form library for Angular. 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!

Highlights

  • Small bundle size due to its modular design
  • Schema-based validation with Valibot
  • Type safety with autocompletion in editor, including fully typed templates
  • Open source and fully tested with 100 % coverage
  • It's fast – fine-grained updates powered by Angular Signals
  • Works with zoneless change detection and OnPush components
  • Minimal, readable and well thought out API
  • Supports all native HTML form fields

Example

Every form starts with the injectForm function. It initializes your form's store based on the provided Valibot schema and infers its types. Next, add the [formischForm] directive to your native <form> element. It handles form validation and submission. Then, create a field with the *formischField directive (or the injectField function in your component class) and bind it to your input with the [formischControl] directive, which syncs the value, the name and aria-invalid attributes, and all event handlers for you.

import { Component } from '@angular/core';
import {
  FormischControl,
  FormischField,
  FormischForm,
  injectForm,
} 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">
      <div *formischField="['email'] of loginForm; let field">
        <input [formischControl]="field" type="email" />
        @if (field.errors(); as errors) {
          <div>{{ errors[0] }}</div>
        }
      </div>
      <div *formischField="['password'] of loginForm; let field">
        <input [formischControl]="field" type="password" />
        @if (field.errors(); as errors) {
          <div>{{ errors[0] }}</div>
        }
      </div>
      <button type="submit">Login</button>
    </form>
  `,
})
export class LoginComponent {
  readonly loginForm = injectForm({ schema: LoginSchema });

  readonly handleSubmit = (output: v.InferOutput<typeof LoginSchema>) => {
    console.log(output);
  };
}

In addition, Formisch offers several functions (we call them "methods") that can be used to read and manipulate the form state. These include focus, getDeepErrors, getErrors, getInput, handleSubmit, insert, move, remove, replace, reset, setErrors, setInput, submit, swap and validate. These methods allow you to control the form programmatically.

Coming from Angular forms

Formisch maps naturally onto what you already know from Reactive Forms and Signal Forms:

Reactive FormsSignal FormsFormisch
[formGroup]="form"[formischForm]="form"
[formControl]="ctrl"[formField]="f.email"[formischControl]="field"
(ngSubmit)="save()"submit(form, action)[formischSubmit]="handleSubmit"
new FormControl('')form(model).emailinjectField(form, { path: [...] })
form.validform().valid()form.isValid()
Validators.requiredschema fn / Zod, ValibotValibot schema

Like Reactive Forms' [formControl] and Signal Forms' [formField], the [formischControl] directive writes the field state into the element – there is no manual [value] or [checked] binding. Validation lives in one place, your Valibot schema, from which all input, output and path types are inferred.

For a step-by-step migration with a side-by-side example, see the migrate from Reactive Forms guide.

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 small 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 Reactive Forms, Signal Forms, ngx-formly, and TanStack Form, see the comparison guide.

Vision

My vision for Formisch is to create a framework-agnostic platform similar to Vite, 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. Together we can make forms even better!

License

This project is available free of charge and licensed under the MIT license.

Contributors

Thanks to all the contributors who helped make this page better!

  • GitHub profile picture of @fabian-hiller

Partners

Thanks to our partners who support the project ideally and financially.

Sponsors

Thanks to our GitHub sponsors who support the project financially.

  • GitHub profile picture of @vasilii-kovalev
  • GitHub profile picture of @UpwayShop
  • GitHub profile picture of @ruiaraujo012
  • GitHub profile picture of @hyunbinseo
  • GitHub profile picture of @nickytonline
  • GitHub profile picture of @kibertoad
  • GitHub profile picture of @caegdeveloper
  • GitHub profile picture of @Thanaen
  • GitHub profile picture of @bmoyroud
  • GitHub profile picture of @ysknsid25
  • GitHub profile picture of @dslatkin