Skip to content

Reference

Components

Two components carry the whole public UI surface: the builder that produces a spec, and the renderer that turns a spec into a working form. Everything else in the package is an internal brick they compose.

The same component is exposed three ways. Props are identical; only the event syntax changes:

<FkFormRender spec={spec} onFormSubmit={(e) => save(e.detail.data)} />

Props are camelCase, events are onXxx handlers.

Renders a spec as a working form.

Prop Type Default Purpose
spec BrickSpec Required. The form to render.
data Record<string, unknown> {} Initial values, keyed by brick key.
context Record<string, unknown> Runtime values for rules and URL interpolation. Never a field value, never emitted.
readOnly boolean false Render every control disabled and hide the built-in action.
disabled boolean false Renders every control unavailable — the disabled attribute throughout.
showSubmit boolean false Render a built-in submit button.
submitLabel string Overrides that button’s label.
editable boolean false Render in builder mode (selection outlines, drop zones). Used by the builder itself.
selectedPath string Which brick shows as selected in editable mode, by path.
locale string Locale used to resolve localized labels and validation messages.

Forms often need values the host knows and the user must not supply: an API host, a tenant plan, an auth token. Pass them as context — a bag kept separate from form data:

<FkFormRender spec={spec} data={values} context={{ apiBase, tenant }} />

Rules and optionsUrl / optionsHeaders interpolation read context and data as one map, context winning on a name clash. Nothing in context is ever validated, written by a value effect, or present in formDataChange / formSubmit. The same prop exists on fk-form-builder, feeding its live preview.

Event Detail Fires when
formDataChange { data, isValid, errors } Any field changes, and after validate()
formSubmit { data, isValid, errors } The form is submitted and valid. An invalid form never emits it, so isValid is always true and errors always empty.
validityChange { valid, errors } On first render and whenever the verdict changes
interface DataChangeDetail {
data: Record<string, unknown>
isValid: boolean
errors: Record<string, string> // keyed by brick key, e.g. "contacts[0].email"
}
await element.validate(): Promise<ValidationResult>
await element.submit(): Promise<ValidationResult>

submit() validates, emits formSubmit only if the form is valid, and returns the verdict either way.

fk-form-render is a form-associated custom element and reports its validity continuously, so a complete screen needs neither a ref nor a validate() call:

<form id="checkout">
<fk-form-render id="form"></fk-form-render>
</form>
<button type="submit" form="checkout">Send</button>

The external button drives the element through the platform, and checkValidity() on the form reflects the spec’s rules. Binding a button’s disabled to validityChange works the same way with no form at all. Where ElementInternals is unavailable the element behaves as it did before.

validate() marks every keyed brick as touched (so errors become visible), validates the flat data, awaits row-level validation on every data grid, merges the results and emits formDataChange. It returns { valid, errors }.

import { useRef } from 'react'
import { FkFormRender } from '@streamline-pulse/formkrafter-react'
const ref = useRef<HTMLFkFormRenderElement>(null)
const verdict = await ref.current?.validate()

The drag & drop editor. It renders its own palette, canvas and property panel.

Prop Type Default Purpose
spec BrickSpec Spec to load into the canvas. Omit to start from an empty form.
data Record<string, unknown> {} Preview values used while editing.
context Record<string, unknown> Runtime values for the live preview, same contract as on the renderer.
locales string[] [] Enables the edit-language selector; panel fields then read and write per locale.
locale string The builder chrome’s own language: pair it with setFkTranslations and the toolbar, palette and panel re-render on the fly — no remount needed. Distinct from the edit language.
Event Detail Fires when
specChange { spec, patches, inverse } Any edit: drop, reorder, config change, undo, redo, import
interface SpecChangeDetail {
spec?: BrickSpec
patches: Operation[] // RFC 6902, forward
inverse: Operation[] // RFC 6902, backward
}

On undo, redo and full-spec replacement, patches and inverse are emitted as empty arrays — the spec itself is authoritative in those cases.

import { useState } from 'react'
import { FkFormBuilder, FkFormRender } from '@streamline-pulse/formkrafter-react'
import '@streamline-pulse/formkrafter-wc/styles.css'
import type { BrickSpec } from '@streamline-pulse/formkrafter-core'
export function Studio() {
const [spec, setSpec] = useState<BrickSpec>()
return (
<>
<FkFormBuilder onSpecChange={(e) => setSpec(e.detail.spec)} />
{spec && (
<FkFormRender spec={spec} onFormSubmit={(e) => console.log(e.detail.data)} />
)}
</>
)
}

A project by Streamline Pulse