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.7.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.7.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 }) |
Destroy any existing controller under the id, build a replacement 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. Missing ids log an error; absent entries log a warning. |
Forms.available |
Shared mutable registry: { [id: string]: FormController }. Prefer newForm() and destroyForm() to manage it. |
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,addCleanup,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 module entrypoint @samline/forms/browser ships types, installs globalThis.Forms, and exposes default plus named registry exports. The direct IIFE subpath does not expose a typed module export.
import Forms from '@samline/forms/browser'
Forms.newForm({ id: 'contact-form' })window.Forms.destroyForm('contact-form')See FormsApi for the full shape.
For an ESM/CommonJS/IIFE comparison, see Entrypoints and module formats.
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.7.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 global browser bundle includes the controller and formatter peer. Inspect the published artifact when bundle size is a constraint.
- CSP: if your site uses a strict Content Security Policy, allow
unpkg.cominscript-src(or self-host the file).