Overview
Forms manages form state for a funnel: values persist locally as a visitor progresses through it, input is validated against a schema, and completed data is saved to Embeddables in the background.Field types
A form is a list of fields, each with one of these types:
Fields support declarative validation:
required, min/max length, min/max value, pattern matching, and custom validators.
Implementation
Install
react as a peer dependency. Install @embeddables/analytics only if you wire Analytics in the React example below.
Define a schema
Each form is one object: anid, an optional name, and its fields.
required, minLength, maxLength, min, max, pattern (a string, not a RegExp), patternFlags, oneOf, and an optional synchronous validations.custom function.
Use
as const satisfies FormSchema on schemas you write by hand — it’s what makes .set(),
.get(), and .getAll() type-safe against your field keys. Schemas generated by the Embeddables
CLI are already typed this way.Quick start
Form API
initForms / getForm options
React
Register Forms throughEmbeddablesProvider’s modules prop — the CLI generates the list, including your form schemas, in embeddables/_dist/modules — then bind fields with useForm / useFormField.
One live form instance is shared per
schema.id across every hook call in your app — calling
useForm with the same formId in two components does not create duplicate forms.Analytics
Forms never imports Analytics. In non-React code, create an Analytics client and pass it asanalyticsInstance:
analytics → forms, so Forms auto-wires core.getAnalyticsInstance() — you don’t pass analyticsInstance yourself. Enable both modules in your config.yaml and the generated modules handles the rest.
When analytics is wired:
- A successful
.set()emitsdata:updatedplus onefield:updatedper changed field .submit()emitsform:submitted
trackError on the result — the values themselves are still saved either way. Omitting analytics only turns off event tracking; the best-effort durable backend save still happens whenever Core has a resolved publishable key.
Errors
A custom validator that throws propagates synchronously out of
.set() / .submit() — everything else is caught and reported on the result instead.
