UI form (React)
A transfer form whose hidden fields, computed fee, warnings and blocked submit all come from the client manifest.
Code: examples/frontend-react
(README). CI builds it on every push.

Run it
npm ci
npm run build # the runtime the example links to
npm run dev -w rule-cascade-example-frontend-react| Do this | The contract responds with |
|---|---|
| Switch Type to International | The SWIFT field appears and becomes required (state rule), and a fee is computed (compute rule) |
| Enter an amount of 10000 or more | A warning that blocks until you tick the acknowledgement |
| Enter an amount above 25000 | An error only a risk-officer may accept |
| Clear the memo | A non-blocking warning |
How it is built
From src/TransferForm.tsx.
The form state is converted to the entity's shape, evaluated with the hook, and drawn:
/** Form state -> the entity shape the contract talks about. Empty inputs become null, not "". */
function toEntity(d: TransferDraft) {
return {
type: d.type,
amount: d.amount.trim() === '' || Number.isNaN(Number(d.amount)) ? null : Number(d.amount),
currency: d.currency,
memo: d.memo,
beneficiary: { name: d.name, country: d.country.toUpperCase(), swiftCode: d.swiftCode.trim() === '' ? null : d.swiftCode.trim() },
};
}const resolutions: Resolution[] = acknowledged.map((rule) => ({ rule, type: 'acknowledge' }));
const request: EvaluationRequest = {
entity: 'Transfer',
operation: 'create',
data: toEntity(draft),
actor: { id: 'u-1', roles: ['teller'] },
resolutions,
};
const { result, allowed, findings, states, computed, findingsFor } = useRuleEvaluation(manifest, request);// Field state comes from `state` rules. A field the contract does not mention keeps the app's default.
const swift = states['/beneficiary/swiftCode'] ?? {};
const fee = computed['/fee'];The manifest is the generated
src/manifest.client.json
so the example runs without a backend. Against a real service, src/App.tsx notes the one change:
const client = createManifestClient({ baseUrl: '/api/rules' });
const manifest = await client.get('acme.payments.transfer');The client manifest has 9 of the 14 rules: the four enforcement: server rules (blocked country,
currency immutable, delete only drafts, four eyes) and the action rule are not in it. A client pass therefore never guarantees the server will accept.
Step by step: Add rule enforcement to a React form.
OpenAPI contract
The payments API description and its ruleset bound to each other - entities reference schemas, operations carry x-rule-cascade, and baseline rules are derived from schema constraints.
API endpoint
A payments API that loads the ruleset at start-up and, on every operation, evaluates, refuses with 422, persists, and runs the commands once.