Guides
Nested forms
An address block, an emergency contact, a set of company details — some
fragments show up in a dozen forms. Rather than copying them, a nested-form
brick references another form. It is resolved and inlined at render time, so
everything downstream — rules, validation, recap — sees one ordinary spec.
Referencing a form
Section titled “Referencing a form”The brick carries a specRef in its configs. What the ref means is entirely up
to the service that resolves it: a URL, a database id, a slug.
{ "type": "panel", "id": "nested-form", "name": "Nested form", "configs": { "key": "address", "label": "Delivery address", "specRef": "address-v2" }}At render time that node is replaced by the referenced form’s children, wrapped in a labelled group.
Wiring the source
Section titled “Wiring the source”The default FetchSpecSourceService treats the ref as a URL and supports
baseUrl, headers, credentials and per-URL caching. To resolve refs from your
own store, provide anything matching the interface:
-
Implement
fetchSpec, returning aBrickSpec.app-startup.ts import { services } from '@streamline-pulse/formkrafter-core'services.specSourceService = {fetchSpec: async (ref) => {const res = await fetch(`/api/forms/${ref}`)if (!res.ok) throw new Error(`Unknown form: ${ref}`)return res.json()},} -
Or configure the built-in one instead of writing your own.
import { services, FetchSpecSourceService } from '@streamline-pulse/formkrafter-core'services.specSourceService = new FetchSpecSourceService({baseUrl: '/api/forms',credentials: 'include',}) -
Render normally.
<fk-form-render>expands nested forms automatically and shows a loading state while it resolves.
Expanding on the server
Section titled “Expanding on the server”Your backend must validate the complete tree, so expand before validating:
import { expandSpec, validateFormData } from '@streamline-pulse/formkrafter-core'
const full = await expandSpec(spec)const verdict = validateFormData(full, payload)hasNestedForms(spec) tells you whether the work is needed at all, which is
worth checking before paying for the expansion on a hot path:
import { hasNestedForms } from '@streamline-pulse/formkrafter-core'
const full = hasNestedForms(spec) ? await expandSpec(spec) : specSafety rails
Section titled “Safety rails”Next steps
Section titled “Next steps”A project by Streamline Pulse