Browser global
Use the browser build when you do not have a bundler and need to integrate the package directly into HTML, Shopify, WordPress, or any traditional template that does not run through a build step.
For every other case (modern apps, bundlers, TypeScript projects), use the main vanilla entrypoint — see Getting started.
Script tag
Section titled “Script tag”<script src="https://unpkg.com/@samline/forms@2.2.2/dist/browser/global.global.js"></script>The bundle is a single IIFE that registers a global object. Place the <script> tag in <head> with defer, or before the user script in <body>.
Global object
Section titled “Global object”The browser build exposes window.Forms (also reachable via globalThis.Forms).
window.Forms = { form, newForm, destroyForm, available}formis the same factory exported by@samline/forms— seeform().newFormanddestroyFormare ergonomic wrappers that keep a registry underForms.available, keyed by the form id.
The factory returns a FormController with the same signatures, semantics, and behaviours as the main vanilla entrypoint — every per-method page in API reference applies.
Minimal example
Section titled “Minimal example”<form id="contact-form"> <input name="email" type="email" /> <input name="message" /> <button type="submit">Send</button></form>
<script src="https://unpkg.com/@samline/forms@2.2.2/dist/browser/global.global.js"></script><script> const contactForm = window.Forms.newForm({ id: 'contact-form', options: { validators: { email: { required: true, pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ }, message: { required: true, minLength: 10 } } } })
contactForm.onSubmit(async (_element, _data, formData) => { await fetch('/api/contact', { method: 'POST', body: formData }) contactForm.reset() })</script>The controller returned by newForm is the same instance stored under Forms.available.contact-form. Use window.Forms.destroyForm('contact-form') later to call destroy() and remove it from the registry.
Registry helpers
Section titled “Registry helpers”| Helper | Purpose |
|---|---|
Forms.newForm({ id, options }) |
Build a controller via Forms.form(id, options) and store it in Forms.available[id]. Logs Form ID is required and returns early if id is missing. |
Forms.destroyForm(id) |
Look up Forms.available[id], call destroy(), and delete the entry. Logs Form ID is required if id is missing, or Form with ID <id> not found if the entry is absent. |
Forms.available |
Read-only view of the active registry: { [id: string]: FormController }. Iterate it to inspect or invoke methods on every live controller. |
Use Forms.form directly when you do not want the registry side-effect (for example, transient controllers in tests).
Surface reference
Section titled “Surface reference”The browser bundle ships only the registry helpers plus the form factory. Every controller method is documented under API reference.
| Global | Purpose |
|---|---|
Forms.form(target, options?) |
Bind a controller to a form (recommended). See form(). |
Forms.newForm({ id, options? }) |
Build + register a controller in Forms.available[id]. |
Forms.destroyForm(id) |
Destroy + unregister a controller by id. |
Forms.available |
Registry of active controllers keyed by id. |
Controller methods
Section titled “Controller methods”The controller returned by form / newForm exposes the methods documented in API reference:
- Lifecycle:
element,reset,destroy. - Submission:
onSubmit,autoSubmit,disableAutoSubmit. - Field observation:
watch,observe,unwatch,subscribe. - Field values:
setValue,getValue,getField,prefill,format,formatAll. - Validation:
validate,revalidate,setErrors,clearErrors. - State and data:
getData,getState,append.
TypeScript users
Section titled “TypeScript users”The browser build does not ship its own types. Reuse the types exported by @samline/forms instead of redeclaring the surface — declare window.Forms against the package’s FormsApi:
import type { FormsApi } from '@samline/forms'
declare global { interface Window { Forms: FormsApi }}See FormsApi for the full shape.
Using the same shape from a bundler
Section titled “Using the same shape from a bundler”If you have a bundler but still want the newForm / destroyForm / available ergonomics — without the IIFE and without window.Forms auto-installed — import the browser singleton from the vanilla entrypoint:
import { browser } from '@samline/forms'
browser.newForm({ id: 'contact-form' })browser.destroyForm('contact-form')browser.available // { 'contact-form': FormController }browser is a module-level singleton with a shared available registry. Spread it into your own global to compose with extra helpers like regex from @samline/formatter:
import { browser } from '@samline/forms'import { regex } from '@samline/formatter'
window.Form = { ...browser, regex }
window.Form.newForm({ id: 'contact-form' })window.Form.destroyForm('contact-form')Because the registry is shared across spreads, window.Form.available and browser.available point to the same object. For independent registries, call form() directly and manage your own map — browser is designed for the one-registry-per-page case. See the Browser registry helpers section in the getting-started guide for the full pattern.
Common pitfalls
Section titled “Common pitfalls”- Pin the version. The CDN URL above uses
@2.2.2. Replace it whenever you upgrade. - The script must be loaded before any code that uses
window.Forms. Place the<script>tag in<head>withdefer, or before the user script in<body>. - No bundler means no tree-shaking. The browser bundle includes the full controller (~5 KB gzipped). That is by design — the alternative would defeat the purpose of a no-bundler setup.
- CSP: if your site uses a strict Content Security Policy, allow
unpkg.cominscript-src(or self-host the file).