Skip to main content

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 apps also need 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: an id, an optional name, and its fields.
Declarative validation rules: 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

set, submit, and validate never reject their promise on a validation failure — always check result.ok / result.errors. Durable saves to the backend are also best-effort: a failed save never rejects .set() or .submit() either.
Bind .set() to change, blur, or a “next step” button — not to every keystroke. .submit() is not idempotent (it fires again on every call), so guard against double-clicks in your UI.

initForms / getForm options

React

Register Forms through EmbeddablesProvider’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 as analyticsInstance:
In React, the CLI orders modules 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() emits data:updated plus one field:updated per changed field
  • .submit() emits form:submitted
If tracking fails, it surfaces as 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.
If analytics is wired, don’t also call analytics.trackEvent(...) yourself for the same submission — you’ll double-count form:submitted.

Errors

A custom validator that throws propagates synchronously out of .set() / .submit() — everything else is caught and reported on the result instead.