Lattice Grid Buy a licence

the form builder

JavaScript form builder: validation and layouts

Lay a form out as fieldsets, rows and columns, give each field a control that fits it, and check it all when the user presses Save. The form below is live: leave the required fields empty and submit it, then fill it in and submit again.

Loading a live form…

Read the form API → Place it in an application layout →

rows and cols

Rows stack, cols sit side by side.

A form is a tree of rows and cols, each nesting the other to any depth. A rows block stacks its children top to bottom. A cols block places them side by side and shares the width equally unless a child sets its own, and the shell module reads the two words the same way. Give a child a percentage width and it takes that share of the row, so three items of 33.3% sit on one line. Below a container width you choose, a cols block stacks into a single column.

what the form gives you

The controls a record needs, and the checks behind them.

Controls

Text, email, password and url inputs, number, textarea, checkbox, toggle, select, radio and checkbox groups, rating, a stage pipeline, chips, a searchable combo, date, date-time and time pickers, one-time-code entry, buttons, and a sanitised rich-text editor.

Layouts

Fieldsets, rows, cols, spacers and separators. Widths in percent, or in pixels, or flexible. Density levels of compact, standard and comfortable, and a form beside a grid follows that grid’s row height.

Labels where you want them

Labels on the left or on top, at a width you set, or hidden from view while staying the field’s accessible name. Set it for the form, a fieldset or one item.

Number formats and units

A number field shows currency, percent or fixed decimals while it is not focused and stores a plain number. A unit-aware field converts what is typed, so 145 psi into a bar field stores the value in bar, and a temperature scale converts too.

Disabled and read-only

One contract for every control. Disabled leaves the tab order and is skipped by validation. Read-only stays focusable and selectable, refuses every change, and still validates and still appears in the values. Set them per item or for the whole form.

Validation

Required fields, built-in rules for email, number, integer and url, a regular expression, or a function that returns a message. Check on submit, on change or on blur. Failures carry aria-invalid and a message beside the field.

Rules that react

visibleWhen, readOnlyWhen and requiredWhen re-run on every committed change, so a field can appear, lock or become required from the value of another.

A stepper for long forms

Give the form a list of steps and it becomes a wizard under a step indicator, horizontal or vertical. Next validates the current step first, Back never does, and the last step reads Finish.

Bound to a grid or the data router

Bind a form to a grid row and it builds one item per column when you declare no layout, tracks which items are dirty, and saves through the grid’s own edit path. Bind it to a data router record and it follows the feed without overwriting what a user is typing.

Files and combos

A file drop area accepts drop, paste or pick, enforces type, size and count, and shows progress with retry and remove. A combo pages and searches a server list, takes avatars, icons and dots on its options, and can allow new entries.

Every framework, and htmx

Wrappers for React, Vue, Svelte and Angular. On an htmx page, a server-rendered form is enhanced in place with a data attribute, keeping its field names and its posts, with validation before the post goes out.

a form from a config

Describe the fields, get the form.

Drop in one script tag, then hand createForm your rows. Every item is in the document and measurable when the call returns, so there is no redraw to wait for. Read the values with getValue(), write them with setValue(), and listen for change or submit.

The form is a separate, opt-in module with a global of its own. See its size next to every other module in the module map.

See the full form API →

<!-- the form module, as a global -->
<script src="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1/modules/form.min.js"></script>

<script>
  var form = LatticeGridForm.createForm(document.querySelector('#deal'), {
    rows: [
      { fieldset: 'Opportunity', cols: [        // cols sit side by side
        { type: 'input', name: 'name', label: 'Name', required: true, width: '33.3%' },
        { type: 'input', name: 'account', label: 'Account', required: true, width: '33.3%' },
        { type: 'combo', name: 'owner', label: 'Owner', options: people, width: '33.3%' }
      ] },
      { type: 'number', name: 'amount', label: 'Amount',
        format: { style: 'currency', currency: 'USD' }, min: 0 },
      { type: 'buttons', align: 'end', items: [
        { name: 'save', label: 'Save', variant: 'primary', buttonType: 'submit' }
      ] }
    ]
  });

  // A submit button validates first, then fires submit with the values.
  form.el.addEventListener('submit', function (e) { save(e.detail.values); });
</script>

Build the form on localhost, free.

Development on localhost needs no licence key, so you can lay out a real record and see it validate before you decide anything. Licence per domain when you ship.