@sonata-innovations/fiber-fbre 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,461 @@
1
+ ---
2
+ title: Fiber Concepts
3
+ applies-to:
4
+ - "@sonata-innovations/fiber-types@^2.2"
5
+ - "@sonata-innovations/fiber-fbre@^3.3"
6
+ - "@sonata-innovations/fiber-fbt@^2.2"
7
+ - "@sonata-innovations/fiber-fbtl@^2.2"
8
+ read-when: "First contact with Fiber: the Flow/Screen/Component model, FlowData, builders vs render engine, conditions/validation/calculations concepts."
9
+ ---
10
+
11
+ <!-- Generated from the Fiber repo's docs/ tree by project/scripts/sync-package-docs.mjs. Do not edit here. -->
12
+ # Fiber Concepts
13
+
14
+ > A high-level conceptual overview of Fiber for AI assistants and newcomers.
15
+ > For integration code and API reference, see the integration guides.
16
+ > For the JSON schema specification, see [`flow-schema.md`](@sonata-innovations/fiber-types/docs/schema/flow-schema.md).
17
+
18
+ ---
19
+
20
+ ## 1. What is Fiber?
21
+
22
+ Fiber is a system for building and rendering data-collection forms. It splits authoring from rendering and offers two authoring surfaces targeted at different audiences:
23
+
24
+ - **FBT (Fiber Tool)** — A visual drag-and-drop builder for power users / form designers. Full feature surface: screens, multi-rule conditions, validation, calculations, reference markup, the complete 30-type component palette. See the [FBT integration guide](@sonata-innovations/fiber-fbt/docs/integration/fbt.md).
25
+ - **FBTL (Fiber Tool Lite)** — A stripped-down, end-user-friendly builder designed to be embedded in parent apps so non-technical end users (clinicians, teachers, etc.) can author their own forms. Five question types + Information Screens + single-rule conditions. Preserves advanced properties from loaded flows without editing them. See the [FBTL integration guide](@sonata-innovations/fiber-fbtl/docs/integration/fbtl.md).
26
+ - **FBRE (Fiber Render Engine)** — A render engine that consumes Flow JSON, renders the interactive form for end users, and outputs collected data ("FlowData") back to the parent application. See the [FBRE integration guide](integration/fbre.md).
27
+
28
+ A fourth library, the **Theme Editor**, is a plug-and-play widget that lets end users visually customize a flow's theme (color scheme, style, palette knobs) and emits the resulting `ThemeConfig`. See the [Theme Editor integration guide](@sonata-innovations/fiber-theme-editor/docs/integration/theme-editor.md).
29
+
30
+ **The problem it solves:** Both form designers and parent-app end users need to create multi-step forms without writing code. Developers need to embed those forms into their applications without building form infrastructure from scratch.
31
+
32
+ The libraries are standard React components — not iframes, not external services. They embed directly into a parent React application with full control over styling, data flow, and lifecycle. **FBT and FBTL produce the same Flow JSON schema**, so any Flow is portable between them (with FBTL preserving — but not editing — power-user features).
33
+
34
+ ### Packages
35
+
36
+ | Package | What it is |
37
+ |---------|-----------|
38
+ | `@sonata-innovations/fiber-types` | Shared TypeScript types — the Flow/FlowData schema as code |
39
+ | `@sonata-innovations/fiber-shared` | Engine layer: condition evaluation, validation, markup conversion, formula evaluation. Used client-side (FBRE, FBT) **and** server-side, so the same rules evaluate identically anywhere |
40
+ | `@sonata-innovations/fiber-fbre` | The render engine |
41
+ | `@sonata-innovations/fiber-fbt` | The full builder |
42
+ | `@sonata-innovations/fiber-fbtl` | The lite builder |
43
+ | `@sonata-innovations/fiber-theme-editor` | The theming widget |
44
+
45
+ Three private (unpublished) packages — a Fastify **server**, an admin **portal**, and a **hosted** form-filling app — make up the hosted platform built on top of the libraries (see Section 13).
46
+
47
+ ---
48
+
49
+ ## 2. Core Data Model
50
+
51
+ ### The Flow JSON
52
+
53
+ The Flow is the interchange format between the builders and the render engine. FBT and FBTL both produce it; FBRE consumes it. It is a plain JSON document that can be stored, transmitted, versioned, and inspected.
54
+
55
+ ### Hierarchy
56
+
57
+ ```
58
+ Flow
59
+ ├── uuid — Unique identifier for the entire flow
60
+ ├── metadata — Name, description, and arbitrary key-value pairs
61
+ ├── config — Runtime settings (mode, theme, navigation, controls, confirmation, summary)
62
+ ├── calculations[] — Optional: named formulas computed over component values
63
+ └── screens[] — Ordered list of screens (pages)
64
+ ├── uuid
65
+ ├── label
66
+ ├── conditions — Optional: show/hide this screen based on rules
67
+ └── components[] — Ordered list of components on this screen
68
+ ├── uuid
69
+ ├── type — One of 30 component types
70
+ ├── properties — Configuration (label, placeholder, options, validation, width, etc.)
71
+ ├── conditions — Optional: show/hide this component based on rules
72
+ └── components[] — Optional: child components (group and repeater types)
73
+ ```
74
+
75
+ ### Identity
76
+
77
+ Every entity (flow, screen, component, calculation) has a UUID. These UUIDs are the stable identity used for:
78
+ - Referencing components in conditions ("show this if *that component* equals X")
79
+ - Cross-field validation ("this field must match *that field*")
80
+ - Reference markup (`${uuid}` tokens resolving to live values)
81
+ - Pre-populating data (matching FlowData entries to their components)
82
+ - Tracking changes in the builder
83
+
84
+ ### Flow Configuration
85
+
86
+ The optional `config` object controls runtime behavior, organized into semantic groups:
87
+
88
+ | Group | Setting | Purpose |
89
+ |-------|---------|---------|
90
+ | _(flat)_ | `mode` | Presentation mode: `"standard"` (default) or `"conversational"` (one question at a time) |
91
+ | `theme` | `color` | Primary accent color (CSS value) |
92
+ | `theme` | `colorScheme` | Built-in palette preset: `"light"` (default) or `"dark"` (replaces the former `darkMode` boolean) |
93
+ | `theme` | `style` | Visual style (`"clean"`, `"outlined"`, …) |
94
+ | `theme` | `background` / `surface` / `text` / `border` | Palette knobs overriding the preset tokens |
95
+ | `theme` | `radius` / `fontFamily` | Corner radius and font family knobs |
96
+ | `theme` | `error` / `success` / `warning` | Semantic state color knobs |
97
+ | `navigation` | `transition` | Screen transition animation type |
98
+ | `navigation` | `allowInvalidTransition` | Allow navigating past screens with validation errors |
99
+ | `controls` | `show` | Show/hide built-in next/back buttons |
100
+ | `controls` | `layout` | Controls layout (`"default"`, `"centered"`, `"inline-full"`, or `"stacked"`) |
101
+ | `controls` | `showStepper` | Show/hide the step indicator |
102
+ | `controls` | `stepperStyle` | Stepper variant (`"default"`, `"dots"`, `"pill"`, `"glow"`, `"bar"`, `"text"`) |
103
+ | `confirmation` | `show` / `title` / `body` | Terminal "thank you" screen shown after submission (see Section 13) |
104
+ | _(flat)_ | `summary` | Show a review screen before final submission |
105
+
106
+ ---
107
+
108
+ ## 3. Screens
109
+
110
+ Screens are the pages of a multi-step form. Users see one screen at a time and navigate between them with next/back buttons.
111
+
112
+ **Key behaviors:**
113
+ - Screens render in array order (first screen in the array is shown first)
114
+ - Navigation is linear by default — next goes forward, back goes backward
115
+ - Screens can be conditionally shown or hidden (see Conditions below) — hidden screens are automatically skipped during navigation
116
+ - Each screen can customize its next and back button labels
117
+ - Screen transitions can be animated (slide, fade, rise, etc.)
118
+
119
+ **Summary screen:** When enabled via `config.summary`, a review screen appears after the last screen showing all collected data before the user confirms submission.
120
+
121
+ ---
122
+
123
+ ## 4. Components
124
+
125
+ Components are the individual form elements within a screen. Fiber has **30 component types** across six categories. This section summarizes each category; the exhaustive per-type property reference lives in [`flow-schema.md`](@sonata-innovations/fiber-types/docs/schema/flow-schema.md).
126
+
127
+ | Category | Types | Purpose |
128
+ |----------|-------|---------|
129
+ | Display (6) | `header`, `text`, `divider`, `callout`, `table`, `computed` | Presentational content: headings, body text (with inline markup), separators, highlighted callouts, static tables, and calculated-value display |
130
+ | Text & Number (3) | `inputText`, `inputTextArea`, `inputNumber` | Free-form text and numeric entry |
131
+ | Selection (8) | `dropDown`, `dropDownMulti`, `checkbox`, `radio`, `toggleSwitch`, `yesNo`, `confirm`, `cardSelect` | Choosing from predefined options — single-select, multi-select, boolean toggles, an acknowledgment checkbox, and rich card-based selection |
132
+ | Date & Time (6) | `date`, `time`, `dateTime`, `dateRange`, `timeRange`, `dateTimeRange` | Point-in-time and range pickers; ranges produce `{ start, end }` values |
133
+ | Interactive (5) | `fileUpload`, `rating`, `slider`, `colorPicker`, `signature` | Richer input widgets: drag-and-drop file upload, star rating, numeric slider, color selection, and signature capture |
134
+ | Containers (2) | `group`, `repeater` | Hold child components (see below) |
135
+
136
+ **Display components** are presentational and are excluded from FlowData — with one exception: `computed` lives in the display category but *does* emit a value (the result of the calculation it displays).
137
+
138
+ **Options:** Selection components that use options store them as `OptionItem` objects: `{ label, value }` where `label` is what the user sees and `value` (a `string` or `number`) is what gets stored in FlowData. Each option may also carry `metadata` — an arbitrary `Record<string, string | number | boolean>` — for machine-readable data (scores, codes, flags) that calculations and parent applications can use.
139
+
140
+ ### Group
141
+
142
+ The `group` type contains its own `components[]` array (containers are recursive — groups can nest). A group is a visual grouping with an optional label; it can be **collapsible** (expand/collapse by the user) and can force its border on or off (`showBorder`).
143
+
144
+ ### Repeater
145
+
146
+ The `repeater` type is the repeatable container: the user can add, fill in, and remove multiple iterations of the repeater's child components (e.g., "Add another phone number"). Its properties include `minIterations` (the minimum number of iterations) and `initialData` (pre-populated iteration values). Groups and repeaters can nest inside each other, but repeaters cannot nest inside repeaters.
147
+
148
+ ### Component Properties
149
+
150
+ Every component has a `properties` object. Some properties are common across types (like `label`, `width`, `validation`), while others are type-specific (like `options` for selection components, `min`/`max` for sliders, `dateFormat` for date pickers). The full per-type property tables are in [`flow-schema.md`](@sonata-innovations/fiber-types/docs/schema/flow-schema.md).
151
+
152
+ ---
153
+
154
+ ## 5. Layout System
155
+
156
+ Each component has an optional `width` property that controls how much horizontal space it occupies:
157
+
158
+ | Width | CSS | Visual |
159
+ |-------|-----|--------|
160
+ | `full` (default) | 100% | Entire row |
161
+ | `half` | 50% | Half row |
162
+ | `third` | 33.33% | One-third |
163
+ | `two-thirds` | 66.67% | Two-thirds |
164
+ | `quarter` | 25% | One-quarter |
165
+ | `three-quarters` | 75% | Three-quarters |
166
+
167
+ **Wrapping behavior:** Components flow left-to-right like text. When components don't fill a row (e.g., two `half` components), they sit side by side. When they exceed a row's width, they wrap to the next line.
168
+
169
+ **Responsive:** Fractional widths are gap-adjusted `calc()` values (e.g. half is `calc(50% - gap/2)`). There is no global full-width collapse breakpoint; a few individual controls (buttons, stepper, yes/no) compact themselves at narrow container widths via CSS container queries — responding to the FBRE container's size, not the viewport.
170
+
171
+ ---
172
+
173
+ ## 6. Condition System
174
+
175
+ Conditions control the visibility of components and screens based on the current values of other components in the flow — or on external **context** values supplied by the parent application.
176
+
177
+ ### Structure
178
+
179
+ ```
180
+ conditions: {
181
+ action: "show" | "hide",
182
+ when: {
183
+ logic: "and" | "or",
184
+ rules: [
185
+ {
186
+ source: "<component-uuid or context key>",
187
+ sourceType: "component" | "context", // optional, defaults to "component"
188
+ operator: "<operator>",
189
+ value: "<test-value>"
190
+ }
191
+ ]
192
+ }
193
+ }
194
+ ```
195
+
196
+ - **`action`** — What to do when the rules evaluate to true: `show` the target or `hide` it
197
+ - **`logic`** — How to combine multiple rules: `and` (all must match) or `or` (any must match)
198
+ - **`source`** — UUID of the component whose value is being tested, or (with `sourceType: "context"`) the key of an external context value
199
+ - **`sourceType`** — Where the source value comes from: `"component"` (default) tests another component's live value; `"context"` tests a value from FBRE's `context` prop, letting parent-supplied data (user role, tenant flags, etc.) drive visibility
200
+ - **`operator`** — One of 19 comparison operators
201
+ - **`value`** — The value to compare against (omitted for operators that don't need it)
202
+
203
+ ### Operators (19)
204
+
205
+ | Category | Operators |
206
+ |----------|-----------|
207
+ | Equality | `equals`, `notEquals` |
208
+ | String | `contains`, `notContains`, `startsWith`, `endsWith` |
209
+ | Empty | `isEmpty`, `isNotEmpty` |
210
+ | Numeric | `greaterThan`, `greaterThanOrEqual`, `lessThan`, `lessThanOrEqual` |
211
+ | Set membership | `isOneOf`, `isNotOneOf` |
212
+ | Array | `includesAny`, `includesAll`, `includesNone` |
213
+ | Boolean | `isTrue`, `isFalse` |
214
+
215
+ ### Runtime Effects
216
+
217
+ When a component or screen is hidden by a condition:
218
+ - It is **not rendered** in the UI
219
+ - Its value is **excluded from FlowData** output
220
+ - Its validation rules are **not enforced** (won't block form submission)
221
+ - If it's a screen, navigation automatically skips it
222
+
223
+ ---
224
+
225
+ ## 7. Validation System
226
+
227
+ Validation enforces rules on component values before the user can proceed to the next screen or submit the form.
228
+
229
+ ### Structure
230
+
231
+ ```
232
+ properties: {
233
+ validation: {
234
+ rules: [
235
+ { type: "<validator>", params: { ... }, message: "Optional custom error" }
236
+ ]
237
+ }
238
+ }
239
+ ```
240
+
241
+ Each rule has a `type` (the validator name), optional `params` (validator-specific configuration), and an optional `message` (custom error text that overrides the default).
242
+
243
+ ### Validators (17)
244
+
245
+ | Validator | Params | Purpose |
246
+ |-----------|--------|---------|
247
+ | `required` | — | Field must have a non-empty value |
248
+ | `email` | — | Valid email format |
249
+ | `phone` | — | Valid phone format |
250
+ | `url` | — | Valid URL format |
251
+ | `minLength` | `{ min }` | Minimum character count |
252
+ | `maxLength` | `{ max }` | Maximum character count |
253
+ | `exactLength` | `{ length }` | Exact character count |
254
+ | `minValue` | `{ min }` | Minimum numeric value |
255
+ | `maxValue` | `{ max }` | Maximum numeric value |
256
+ | `pattern` | `{ pattern }` | Regex pattern match |
257
+ | `minSelected` | `{ min }` | Minimum selected options (checkbox/multi-select) |
258
+ | `maxSelected` | `{ max }` | Maximum selected options |
259
+ | `fileType` | `{ types[] }` | Allowed MIME types |
260
+ | `fileSize` | `{ max }` | Maximum file size in bytes |
261
+ | `contains` | `{ text }` | Must contain substring |
262
+ | `excludes` | `{ text }` | Must not contain substring |
263
+ | `matchesField` | `{ field }` | Must match another component's value (by UUID) |
264
+
265
+ ### Validation Timing
266
+
267
+ - Validation runs when the user attempts to navigate forward (next button or submission)
268
+ - If `config.navigation.allowInvalidTransition` is false (default), the user cannot proceed past an invalid screen
269
+ - Components hidden by conditions are not validated
270
+ - `matchesField` enables cross-field validation (e.g., "confirm email must match email")
271
+
272
+ ---
273
+
274
+ ## 8. Calculations
275
+
276
+ Calculations are named formulas defined at the flow level (`Flow.calculations[]`) that compute numeric results from component values as the user fills in the form.
277
+
278
+ - Each calculation has a `uuid`, `label`, and a `formula` — an expression over component values referenced by UUID, supporting arithmetic and aggregate functions
279
+ - Optional formatting: `format` (`"number"`, `"currency"`, `"percentage"`), `decimalPlaces`, `currencySymbol`
280
+ - Results update live as the user types
281
+ - The `computed` display component shows a calculation's result inline in the form
282
+ - `${uuid}` reference markup (see Section 10) can interpolate calculation results into text
283
+ - Results are emitted in FlowData under a top-level `calculations[]` array (see Section 9)
284
+
285
+ The formula engine lives in `@sonata-innovations/fiber-shared`, so calculations evaluate identically in FBRE, in FBT's preview, and on the server.
286
+
287
+ ---
288
+
289
+ ## 9. Data Flow: End to End
290
+
291
+ ### Authoring Phase (FBT or FBTL)
292
+
293
+ 1. A form author opens **FBT** (power users) or **FBTL** (non-technical end users) in a parent application
294
+ 2. They assemble the flow — FBT via multi-screen drag-and-drop with the full component palette; FBTL via a flat, one-question-per-screen list limited to five question types plus Information Screens
295
+ 3. The builder produces a Flow JSON object via the `onFlowChange` callback (FBT also exposes `exportFlow()` on the store)
296
+ 4. The parent application saves the Flow JSON (to a database, file, API, etc.)
297
+
298
+ FBTL emits a flow with one component per screen (conversational mode) and promotes component-level conditions to screen-level conditions on emit. Loading a multi-screen flow into FBTL flattens it into the one-per-screen shape on the next save, while preserving all advanced properties (calculations, reference markup, multi-rule conditions, etc.) that FBTL itself cannot edit. The resulting JSON remains fully interoperable with FBT.
299
+
300
+ ### Rendering Phase (FBRE)
301
+
302
+ 1. The parent application loads the saved Flow JSON and passes it to FBRE (as a prop, or via one of the remote modes — see Section 13)
303
+ 2. FBRE renders the form screen by screen
304
+ 3. The end user fills in the form, navigating between screens
305
+ 4. On the final screen, the user submits the form
306
+ 5. FBRE calls `onFlowComplete` with the collected FlowData
307
+
308
+ ### FlowData Structure
309
+
310
+ FlowData mirrors the Flow hierarchy but contains only collected values:
311
+
312
+ ```
313
+ FlowData
314
+ ├── uuid — Same as the flow's UUID
315
+ ├── metadata — Same as the flow's metadata
316
+ ├── calculations[] — Present when the flow defines calculations
317
+ │ ├── uuid — Calculation UUID
318
+ │ ├── label — Calculation label
319
+ │ ├── value — Computed numeric result (or null)
320
+ │ └── formattedValue — Formatted string (e.g., "$1,250.00"), when computed
321
+ └── screens[]
322
+ ├── uuid — Screen UUID
323
+ ├── label — Screen label
324
+ └── components[]
325
+ ├── uuid — Component UUID
326
+ ├── type — Component type
327
+ ├── label — Component label
328
+ ├── value — The collected value (type varies by component)
329
+ └── components[][] — Containers only: child data, one inner array per iteration
330
+ ```
331
+
332
+ **Containers in FlowData:** `group` and `repeater` components appear as their own entries carrying a nested `components` field typed `ComponentData[][]` — an array of iterations, each an array of child component data. A group always has exactly one iteration; a repeater has one inner array per iteration the user filled in.
333
+
334
+ **What's excluded from FlowData:**
335
+ - Display components (`header`, `text`, `divider`, `callout`, `table`) — they don't collect data (`computed` *is* included, with its calculated value)
336
+ - Components hidden by conditions — their values are not relevant
337
+ - Screens hidden by conditions — all their components are excluded
338
+
339
+ The full output schema is documented in [`flow-data-schema.md`](@sonata-innovations/fiber-types/docs/schema/flow-data-schema.md).
340
+
341
+ ---
342
+
343
+ ## 10. Theming and Presentation
344
+
345
+ ### CSS Custom Properties
346
+
347
+ FBRE uses CSS custom properties for theming. Parent applications can override these on the `.fbre-container` element:
348
+
349
+ - **Colors:** `--fbre-theme-color`, `--fbre-error`, `--fbre-text`, `--fbre-border`, `--fbre-bg`, `--fbre-surface` (and light/dark/secondary variants)
350
+ - **Sizing:** `--fbre-radius` (border radius)
351
+ - **Typography:** `--fbre-font` (font family)
352
+ - **Animation:** `--fbre-transition-duration`, `--fbre-transition-easing`
353
+
354
+ ### Color Scheme
355
+
356
+ Selected via the `theme` prop on FBRE (`{ colorScheme: "dark" }`) or `config.theme.colorScheme` in the Flow JSON (default `"light"`). The prop takes precedence. The scheme seeds a built-in palette preset (all `--fbre-*` tokens shift to light- or dark-appropriate values); palette knobs and raw CSS-variable overrides then layer on top. Replaces the former `darkMode` boolean.
357
+
358
+ For the full theming model — palette knobs, the override precedence, and the complete `--fbre-*` token catalog — see the [FBRE Theming Guide](features/fbre-theming.md).
359
+
360
+ ### Screen Transitions
361
+
362
+ Six animation types for transitions between screens:
363
+
364
+ | Type | Effect |
365
+ |------|--------|
366
+ | `none` | Instant switch (default) |
367
+ | `slide` | Horizontal slide left/right |
368
+ | `fade` | Cross-fade |
369
+ | `slideFade` | Slide combined with fade |
370
+ | `rise` | Vertical rise with fade |
371
+ | `scaleFade` | Scale up/down with fade |
372
+
373
+ Set via `config.navigation.transition` in the Flow JSON.
374
+
375
+ ### Inline Markup
376
+
377
+ `text` components and `detail` property fields support lightweight inline formatting:
378
+ - `[b]bold[/b]` renders as **bold**
379
+ - `[i]italic[/i]` renders as *italic*
380
+ - `[s size="sm|lg|xl"]sized text[/s]` renders at a preset font size
381
+ - `[l href="url"]link text[/l]` renders as a hyperlink
382
+
383
+ The markup → HTML converter lives in `@sonata-innovations/fiber-shared`, shared by FBRE (render), FBT (preview), and the server.
384
+
385
+ ### Reference Markup
386
+
387
+ `${...}` tokens interpolate live values into text. A token is resolved in order against: **calculations** (by UUID) → **component values** (by UUID) → **context** (by key, from FBRE's `context` prop). Unresolvable references render as empty. This powers dynamic text like "Your total is ${calc-uuid}" or "Welcome back, ${userName}".
388
+
389
+ ---
390
+
391
+ ## 11. Extension Points
392
+
393
+ ### Custom Presets
394
+
395
+ Presets are pre-configured component templates that appear in FBT's component palette. They let teams create reusable field patterns (e.g., "Phone Number" = inputText with phone validation and placeholder).
396
+
397
+ A preset is **serializable data** — a `PresetData` object (`{ type, label, icon, components }`) where `components` is a plain array of `Component` JSON. Because presets are data, they can be stored anywhere JSON can: in code, in a file, or in a database. When a preset is dropped onto the stage, FBT regenerates every UUID in the copy, so the same preset can be used many times without ID collisions.
398
+
399
+ ### Custom Templates
400
+
401
+ Templates are complete pre-built flows (multiple screens with components). They let teams provide starting points for common form patterns (e.g., "Employee Onboarding", "Patient Intake").
402
+
403
+ A template is likewise data — a `TemplateData` object (`{ type, label, icon, flow }`) holding a complete `Flow`. Loading a template replaces the current flow in FBT, with all UUIDs regenerated.
404
+
405
+ ### Data-First Design
406
+
407
+ Presets and templates being plain data (rather than code) is what enables server-side storage: the hosted platform stores tenant-scoped custom presets and templates in the database and serves them to FBT at load time. Older factory-function definitions (functions returning fresh `Component`/`Flow` objects) are still accepted for back-compat, but data-based definitions are the current model.
408
+
409
+ See [`custom-presets-and-templates.md`](@sonata-innovations/fiber-fbt/docs/features/custom-presets-and-templates.md) for the full authoring guide.
410
+
411
+ ---
412
+
413
+ ## 12. Key Architectural Decisions
414
+
415
+ ### Zustand Stores
416
+
417
+ FBT, FBTL, and FBRE all use Zustand for state management. Each component instance creates its own isolated store, so multiple instances on the same page don't conflict.
418
+
419
+ ### Shared Engine Layer
420
+
421
+ Condition evaluation, validation, markup conversion, and formula evaluation live in `@sonata-innovations/fiber-shared` — a dependency of both the client libraries and the server. This is the key to the "same rules evaluate anywhere" mental model: a condition behaves identically in FBRE running in a browser and in the server evaluating a session screen-by-screen.
422
+
423
+ ### Component Registry
424
+
425
+ FBRE uses a registry pattern to map component types to their React renderers. Each component type registers itself, and a dispatcher looks up the correct renderer at runtime. This makes the system extensible — adding a new component type means registering a new entry.
426
+
427
+ ### Flattened Store Architecture
428
+
429
+ Components are stored in a flat `Record<string, Component>` map (keyed by UUID), not nested inside screens. Separate index maps (`screenComponents`, `groupComponents`) track parent-child relationships. This makes lookups O(1) and avoids deep cloning when updating nested structures.
430
+
431
+ ### No Runtime Auto-Migration
432
+
433
+ There is no runtime auto-migration: legacy (pre-v2) flows — old condition/validation formats, group `layout` strings, deprecated component types — must be migrated to the current schema before being loaded.
434
+
435
+ ### Container Queries
436
+
437
+ Where components adapt to narrow spaces (compact button/stepper sizing, yes/no stacking), they use CSS container queries rather than media queries. The layout responds to the FBRE container's width, not the browser viewport — critical for embedded use cases where the form may occupy a sidebar or modal.
438
+
439
+ ---
440
+
441
+ ## 13. Integration Modes and Platform
442
+
443
+ ### FBRE's Three Integration Modes
444
+
445
+ FBRE accepts a flow through one of three mutually exclusive prop shapes:
446
+
447
+ 1. **Local flow** — `<FBRE flow={flowJson} />`. The parent owns storage and passes the Flow JSON directly. The simplest and most common mode.
448
+ 2. **Remote flow** — `<FBRE flowId="..." apiEndpoint="..." />`. FBRE fetches a published flow from a Fiber server and renders it client-side. All evaluation (conditions, validation, calculations) still happens in the browser.
449
+ 3. **Server-driven** — `<FBRE flowId="..." sessionEndpoint="..." />`. FBRE starts a session and receives one screen at a time; the server evaluates conditions and validation between screens (using the same `fiber-shared` engines) and assembles the final FlowData. The full flow definition never reaches the client — useful for sensitive branching logic and server-side integrations between screens.
450
+
451
+ ### Conversational Mode
452
+
453
+ Setting `config.mode: "conversational"` (or the FBRE `mode` prop) renders the flow one question at a time in a chat-like presentation instead of the standard screen-per-page layout. Same Flow JSON, same condition/validation/FlowData behavior — only the presentation changes. FBTL authors flows in this shape by default.
454
+
455
+ ### Confirmation Screen
456
+
457
+ `config.confirmation` (`{ show, title, body }`) defines a terminal "thank you" screen shown after submission. Title and body support inline and reference markup, resolved against collected values and context. The parent can also drive it dynamically: `onFlowComplete` may return nothing (`void`), a `Promise` (the submit button stays in-flight until it settles), or a `ConfirmationResult` (directly or via the Promise) that overrides the configured message — e.g., to show a server-generated reference number. A rejected Promise surfaces as a completion error and the confirmation is not shown.
458
+
459
+ ### Hosted Platform
460
+
461
+ The private packages (**server**, **portal**, **hosted**) form a hosted form service built on the public libraries: tenant-scoped flow storage and publishing, custom preset/template storage, file upload hosting, submission management, session-based server-driven rendering, and a hosted form-filling app. The libraries remain free and self-sufficient; the platform is the product layered on top.