Guides
Dynamic rules
Most real forms are conditional: a field appears only for a certain country, a section unlocks once a box is ticked, a total recomputes as quantities change. In FormKrafter that behavior lives in the spec as rules, so it survives being stored, migrated and re-validated on the server.
Anatomy of a rule
Section titled “Anatomy of a rule”A rule has a trigger and a list of effects. When the trigger evaluates to
strictly true, its effects apply:
type Rule = { name: string type: 'jsonLogic' | 'javaScript' logic?: RulesLogic // when type is 'jsonLogic' code?: string // when type is 'javaScript' effects?: Effect[]}Each effect targets one property of the brick:
| Target | Type | Effect |
|---|---|---|
hidden |
boolean | removes the field from view and from validation |
disabled |
boolean | renders it read-only |
required |
boolean | makes it mandatory conditionally |
value |
computed | overwrites the field’s value |
Two ways to write the trigger
Section titled “Two ways to write the trigger”JSON Logic — the default
Section titled “JSON Logic — the default”Serializable, inspectable, safe to store and diff:
{ "name": "eu-only", "type": "jsonLogic", "logic": { "!": { "in": [{ "var": "country" }, ["FR", "DE"]] } }, "effects": [ { "property": { "target": "hidden", "type": "boolean" }, "boolean": true } ]}JavaScript — the escape hatch
Section titled “JavaScript — the escape hatch”When the logic gets awkward to express declaratively:
return dataMap.quantity > 10 ? 'GOLD' : 'STANDARD'dataMap is the whole form’s data, keyed by brick key. The builder’s rule
editor autocompletes dataMap. with your form’s actual field keys.
The sandbox
Section titled “The sandbox”JavaScript rules are never passed to eval. They are parsed with Acorn and
walked by an AST interpreter:
- CSP-safe — no
unsafe-evaldirective needed anywhere in your app. - Isolated — only
dataMap(andvalueinside custom validators), plus whitelisted builtins:Math,JSON,String,Number,Date,Array,Object,Boolean. Nofetch, noglobalThis, noconstructoror__proto__escape. - Expressive enough — expressions, ternaries,
const/let,if/return, template literals, optional chaining, arrow functions (.filter(x => …)). - Bounded — unsupported syntax is rejected upfront and runaway code is cut by an execution budget.
You can run the same sandbox yourself:
import { runSandboxed } from '@streamline-pulse/formkrafter-core'
runSandboxed('return items.filter((i) => i > 2)', { items: [1, 2, 3] })// → [3]Rules and validation
Section titled “Rules and validation”A brick hidden by a rule — or nested inside a hidden parent — is skipped by
validation, in the browser and in validateFormData on your server. This is
what stops a conditionally hidden required field from blocking a submission
forever.
Next steps
Section titled “Next steps”A project by Streamline Pulse