Configuration
Every option, default, and rule type — attributes, autoValidate, autoSubmit, validators, and more.
This page walks you through installing @samline/forms and running a working example. By the end you’ll have a bound controller, validated inputs, and a fetch-based submit.
npm, pnpm, yarn or bun.npm install @samline/formspnpm add @samline/formsyarn add @samline/formsbun add @samline/formsDrop this in any modern bundler or TypeScript project:
<form id="contact-form"> <input name="email" type="email" /> <input name="message" /> <button type="submit">Send</button></form>import { form } from '@samline/forms'
const contact = form('contact-form', { validators: { email: { required: { value: true, message: 'Email is required.' }, pattern: { value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: 'Enter a valid email address.' } }, message: { required: true, minLength: 10 } }})
contact.onSubmit(async (_element, _data, formData) => { await fetch('/api/contact', { method: 'POST', body: formData })})
contact.validate()What this does:
contact-form.css-filled and css-error attributes on fields so you can style them with CSS — see CSS styling.email and message on every change and on submit.onSubmit) and hands a real FormData instance to your handler.If you do not have a bundler, drop a single <script> and use the window.Forms global:
<form id="contact-form"> <input name="email" type="email" /> <button type="submit">Send</button></form>
<script src="https://unpkg.com/@samline/forms@2.2.2/dist/browser/global.global.js"></script><script> const contact = window.Forms.newForm({ id: 'contact-form', options: { validators: { email: { required: true, pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ } } } })
contact.onSubmit(async (_form, _data, formData) => { await fetch('/api/contact', { method: 'POST', body: formData }) })</script>See the Browser global reference for the full surface, including Forms.newForm, Forms.destroyForm, and the Forms.available registry.
If you have a bundler and want the same { newForm, destroyForm, available } ergonomics as the IIFE — but without window.Forms auto-installed — import the browser singleton from the vanilla entrypoint:
import { browser } from '@samline/forms'import { regex } from '@samline/formatter' // optional, from your project
window.Form = { ...browser, regex }
window.Form.newForm({ id: 'contact-form', options: { validators: { email: { required: true, pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ } } }})
window.Form.destroyForm('contact-form')console.log(window.Form.available) // { 'contact-form': FormController }browser is an object you can spread into your own globals or use directly. Because newForm and destroyForm close over the singleton’s available map, every spread shares the same registry — window.Form.available, browser.available, and any other spread all point to the same object, and destroyForm on one updates the others.
If you need multiple independent registries, call the form() factory directly and keep your own map of controllers — browser is designed for the common case of one registry per page.
See the Browser global reference → Using the same shape from a bundler for more, and the TypeScript reference → FormsApi for the exact shape.
Configuration
Every option, default, and rule type — attributes, autoValidate, autoSubmit, validators, and more.
API reference
Function-by-function documentation with TypeScript signatures, return shapes, and behaviour tables.
TypeScript
Every exported type — FormController, FormControllerOptions, ValidationSchema, callbacks, helpers.
Examples
End-to-end patterns: login, server errors, multi-step, autosave, formatting, and SPA cleanup.