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 Values cannot be changed: readonly on text controls, disabled elsewhere; hides 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.
annotations FieldAnnotations — Reviewer notes, keyed by brick key: { incorrect?, comment? }. Shown under the matching fields.
editableKeys string[] — When set, only these keys stay editable; every other field renders read-only.
notes FieldAnnotations — An earlier review’s verdicts, shown as muted reminders under the fields, in any mode.

A reviewer’s verdict (see <fk-form-review> below) comes back as a map of annotations. Hand it to the renderer and lock everything that was not flagged:

import { flaggedKeys } from '@streamline-pulse/formkrafter-core'
<FkFormRender spec={spec} data={previous} annotations={reviews} editableKeys={flaggedKeys(reviews)} />

Flagged fields show a “To correct” badge and the comment under the control. editableKeys applies to inputs and collections; the rows of an editable collection stay fully editable.

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.

Text and text-area fields can suggest template variables while typing. Pass the list through the form context as _variables; typing {{ opens the list, the characters after it filter by name or label, Enter or a click inserts {{name}}. Set variables: false in a brick’s configs to opt one field out. Password fields never suggest.

render.context = {
_variables: [
{ name: 'S1$email', label: 'Request · Email' },
{ name: 'RECORD_ID', label: 'Case reference' },
],
};
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()

Renders a filled form read-only and lets a reviewer flag each field as incorrect and comment it. It delegates rendering to <fk-form-render>, so nested forms, rules and localized labels behave the same.

Prop Type Default Purpose
spec BrickSpec — Required. The form that was filled.
data Record<string, unknown> {} The values under review.
context Record<string, unknown> — Same as on the renderer.
reviews FieldAnnotations {} Annotations to start from, for instance a draft of this pass.
notes FieldAnnotations — The previous pass’s verdicts, shown as reminders so the reviewer looks at those fields again; the new review starts empty.
locale string — Locale for labels.
Event Detail Fires when
reviewChange { reviews, flagged } A flag or a comment changes. reviews is the full map, flagged the keys marked incorrect.
<FkFormReview spec={spec} data={submitted} onReviewChange={(e) => setReviews(e.detail.reviews)} />

Inside a data grid every cell is reviewed on its own: keys read contacts[0].email, the same scheme as validation errors, and the grid itself can also be flagged as a whole. editableKeys accepts those cell keys too; when only some cells are listed, the row actions (add, remove, reorder) are locked. The native renderer offers the same through FormReview and the annotations, notes and editableKeys props of FormRenderer.

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