Form.js is a lightweight browser library for automatic form validation and submission.
Category: Hanzo Ecosystem Related Skills: hanzo/hanzo-checkout.md, hanzo/hanzo-commerce.md
Form.js is a lightweight browser library for automatic form validation and submission. It intercepts native HTML form submit events, emits lifecycle events (init, submit, done), and provides an XHR helper for posting form data as JSON. Zero dependencies beyond little-emitter. Written in CoffeeScript, ships as both CommonJS and ES module bundles. Designed as a companion to Hanzo's mailing list and lead capture forms.
little-emitter (tiny event emitter, ~150 bytes)shortcake + cake-bundle using requisite)lib/form.cjs.js) + ES module (lib/form.es.js)form.js v0.2.0Repo: github.com/hanzoai/form.js (branch: master, 16 stars).
<form> element in the page -- the library auto-discovers the last form on the page at load timegetElementsByTagName to find the last <script> and <form>)| Item | Value | |------|-------| | npm package | form.js | | Version | 0.2.0 | | Default branch | master | | License | BSD-3-Clause | | Language | CoffeeScript | | CJS entry | lib/form.cjs.js | | ES entry | lib/form.es.js | | Global | window.Inform | | Runtime dep | little-emitter only | | Repo | github.com/hanzoai/form.js |
<form action="/subscribe" method="POST">
<input type="email" name="email" placeholder="Email" required />
<input type="text" name="firstName" placeholder="First Name" />
<button type="submit">Subscribe</button>
<script src="https://unpkg.com/[email protected]/lib/form.cjs.js"></script>
<script>
var Inform = window.Inform
// Listen for form submission (prevents default, gives you control)
Inform.on('inform-submit', function(done, event) {
var formData = new FormData(event.target)
var data = {}
formData.forEach(function(value, key) { data[key] = value })
fetch('/api/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
.then(function() { done() }) // Call done() to fire the 'done' event
.catch(function(err) { console.error(err) })
})
// Listen for successful submission
Inform.on('inform-done', function(event) {
alert('Thanks for subscribing!')
})
</script>
</form>
npm install form.js
// ES module
import Inform from 'form.js'
Inform.on('inform-submit', function(done, event) {
// Handle submission
done()
})
┌────────────────────────────────────────┐
│ HTML Page │
│ │
│ <form> │
│ <input name="email" /> │
│ <button type="submit">Go</button> │
│ │
│ <script src="form.js"></script> │
│ </form> │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Inform (EventEmitter) │ │
│ │ │ │
│ │ Events: │ │
│ │ 1. init-inform-script ─────> script tag ref │
│ │ 2. init-inform-form ─────> form element ref │
│ │ 3. init-inform-inputs ─────> input elements │
│ │ 4. init-inform-submits ─────> submit buttons │
│ │ 5. inform-submit ─────> (done, event) │
│ │ 6. inform-done ─────> completion │
│ └──────────────────────────────────┘ │
└────────────────────────────────────────┘
<form> and <script> tagsinit-inform-script -- Fires with the script element referenceinit-inform-form -- Fires with the form element referenceinit-inform-inputs -- Fires with array of input/select elements (excludes submit buttons)init-inform-submits -- Fires with array of submit buttons (input[type=submit] + button[type=submit])inform-submit -- Fires on form submit with (done, event). Call done() to proceed to inform-done. The default form submit is prevented.inform-done -- Fires after done() is called, re-dispatches the native submit eventThe library exposes window.Inform as an event emitter with standard methods:
Inform.on(event, callback) -- Listen for an eventInform.off(event, callback) -- Remove a listenerInform.emit(event, ...args) -- Emit an eventInform.Events -- Map of event name constantsInform.events -- Same map (alias)A minimal XMLHttpRequest wrapper with a post(url, headers, payload, callback) method. Used internally for legacy AJAX form submission. Supports IE via ActiveXObject fallback.
| File | Purpose | |------|---------| | src/index.coffee | Main library: auto-init, event lifecycle, form interception | | src/xhr.coffee | XHR POST helper class |
form.js/
package.json # npm config, v0.2.0
Cakefile # Build/test tasks (shortcake + cake-bundle)
README.md # Minimal README
LICENSE # BSD-3-Clause
.travis.yml # Travis CI config
src/
index.coffee # Main library (event emitter + form interception)
xhr.coffee # XMLHttpRequest POST helper
test/
test.coffee # Selenium WebDriver integration tests
test.html # Test harness HTML page
inform.js # Built test bundle
util.coffee # Test utilities
ci-config.coffee # Cross-browser Sauce Labs config
jquery-1.11.3.min.js # jQuery for test assertions
lib/ # Built output (generated)
form.cjs.js # CommonJS bundle
form.es.js # ES module bundle
# Install dependencies
npm install
# Build (generates lib/form.cjs.js and lib/form.es.js)
cake build
# Run tests (starts static server + Selenium + Mocha)
cake test
# Clean build artifacts
cake clean
| Issue | Cause | Solution | |-------|-------|----------| | window.Inform is undefined | Script loaded before form.js bundle | Ensure form.js script tag is loaded | | Form submits normally (page reload) | Script tag not inside/after the form | Place <script> inside the <form> or after it | | inform-submit not firing | Form has no submit button | Add <button type="submit"> or <input type="submit"> | | Events fire twice | DOMContentLoaded and load both trigger init | Library guards against double-init internally | | done() not working | Not calling the first argument of inform-submit callback | Signature is function(done, event) -- call done() to proceed |
hanzo/hanzo-checkout.md - Full checkout widget (uses similar patterns)hanzo/hanzo-commerce.md - Hanzo Commerce frontendLast Updated: 2026-03-13 Category: Hanzo Ecosystem Related: forms, validation, submission, lead-capture, mailing-list Prerequisites: Browser environment, HTML form element