Skip to main content

Overview

Forms handles the state of a multi-step form: it saves answers locally as the visitor moves through it, checks each input against your rules, and sends the completed data to Embeddables in the background. The walkthrough below covers install and setup. For every option, method, hook, and analytics event, open the reference.

Field types

A form is a list of fields, each with one of these types: Each field can have validation rules: required, min/max length, min/max value, pattern matching, and your own custom checks.

Implementation

Install

The React bindings work with React 18 or newer. Add Forms with the CLI so em build includes it in the generated modules file — the React example imports that file. For form event tracking (data:updated, form:submitted), add @embeddables/analytics with the CLI as well; see Analytics.

Define a schema

Each form is one object: an id, an optional name, and its fields.
Available validation rules: required, minLength, maxLength, min, max, pattern (a string, not a RegExp), oneOf, and an optional validations.custom function. pattern is the regular expression source without delimiters. To apply regex flags, put them in an optional leading (?flags) prefix on the pattern itself — for example '(?u)^\\p{L}+$' for a Unicode-aware match. Any of dgimsuvy are accepted; g and y are stripped before the pattern compiles, so validation stays stateless across repeated checks.
Add as const satisfies FormSchema to schemas you write by hand — it makes .set(), .get(), and .getAll() aware of your field keys. CLI-generated schemas already have this.
With a schema defined, pick your setup:
Follow the example below: wrap your app once, then connect each field with useForm and useFormField.
If you used the CLI, the modules import in the example already turns Forms on — you rarely need extra wiring. Schemas come from Core config (config.forms); optional per-form settings go on forms() (customValidations, serverFormData, and Analytics). For useForm, useFormField, and useFormErrors, see hooks.
Save answers when the visitor leaves a field (or taps “next”), not on every keystroke — the example does this for you. Saving too often creates noisy analytics and extra backend traffic.
Two screens that use the same form id share one form — you will not get duplicate copies.
Show a short loading state until the form is ready (form is empty at first). If it never becomes ready, add Forms with the CLI so it is included in the generated modules file.

Analytics

Forms does not depend on Analytics. In non-React code, set up Analytics first, then pass it into initForms when you connect the two (see Analytics below):
In React, adding both SDKs with the CLI connects Analytics to Forms for you — no extra steps. When Analytics is connected:
  • A successful .set() emits data:updated plus one field:updated per changed field
  • .submit() emits form:submitted
If tracking fails, you get a trackError on the result — the values are still saved. Leaving analytics out just turns off event tracking; the backend save still happens as long as Core has a publishable key.
When Analytics is connected to Forms, do not also send the same events yourself — you will count them twice (data:updated, field:updated, and form:submitted).

Errors

If one of your custom validators throws, that error comes straight out of .set() / .submit(). Everything else is caught and reported on the result instead.