npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sonata-innovations/fiber-fbre

v2.0.4

Published

Fiber Render Engine — renders Flow JSON forms with conditional logic, validation, and screen transitions

Downloads

437

Readme

@sonata-innovations/fiber-fbre v2

Fiber Render Engine — consumes Flow JSON and renders data collection forms. Handles conditional logic, validation, screen transitions, and outputs collected FlowData back to the parent application.

Quick Start

Local Mode

Pass a Flow object directly:

import { FBRE } from "@sonata-innovations/fiber-fbre";
import "@sonata-innovations/fiber-fbre/styles";

function App() {
  return (
    <FBRE
      flow={myFlow}
      screenIndex={0}
      onFlowComplete={(data) => console.log(data)}
    />
  );
}

Remote Mode

Fetch a published flow from a Fiber API:

import { FBRE } from "@sonata-innovations/fiber-fbre";
import "@sonata-innovations/fiber-fbre/styles";

function App() {
  return (
    <FBRE
      flowId="your-flow-id"
      apiEndpoint="https://your-api.example.com/api/v1"
      apiKey="your-api-key"
      onFlowComplete={(data) => console.log(data)}
    />
  );
}

Server-Driven Mode

Session-based rendering where the server evaluates conditions and validation:

import { FBRE } from "@sonata-innovations/fiber-fbre";
import "@sonata-innovations/fiber-fbre/styles";

function App() {
  return (
    <FBRE
      sessionEndpoint="https://your-api.example.com/api/v1/public/sessions"
      flowId="your-flow-id"
      onFlowComplete={(data) => console.log(data)}
    />
  );
}

Installation

npm install @sonata-innovations/fiber-fbre

Then import the CSS in your app entry point:

import "@sonata-innovations/fiber-fbre/styles";

Peer dependencies: react ^18.0.0 || ^19.0.0, react-dom ^18.0.0 || ^19.0.0

Runtime dependencies: zustand, @sonata-innovations/fiber-types, @sonata-innovations/fiber-shared

API

<FBRE /> Props

FBRE supports three rendering modes, selected by which props you pass. The modes are mutually exclusive.

Local Mode

Render a Flow object you already have in memory.

| Prop | Type | Required | Description | |------|------|----------|-------------| | flow | Flow | Yes | Flow JSON object | | data | FlowData | No | Pre-populated form data | | mode | FlowModeType | No | Override form mode ("standard" | "conversational") | | screenIndex | number | No | Initial screen index (default 0) | | theme | ThemeConfig | No | Override theme settings (merged over flow.config.theme) | | navigation | NavigationConfig | No | Override navigation settings (merged over flow.config.navigation) | | controls | ControlsConfig | No | Override controls settings (merged over flow.config.controls) | | context | Record<string, string \| boolean \| number> | No | External context values for condition evaluation and calculations | | storeRef | MutableRefObject<StoreApi<FBREStoreState> \| null> | No | Ref to access the Zustand store | | onFlowComplete | (data: FlowData) => void | Yes | Called when the user completes the flow | | onScreenChange | (index: number, data: FlowData) => void | No | Called on screen navigation | | onScreenValidationChange | (index: number, data: any) => void | No | Called when screen validity changes |

Remote Mode

Fetch a published flow from a Fiber API by ID.

| Prop | Type | Required | Description | |------|------|----------|-------------| | flowId | string | Yes | ID of the published flow | | apiEndpoint | string | Yes | Base URL of the Fiber API | | apiKey | string | No | API key for authentication | | data | FlowData | No | Pre-populated form data | | mode | FlowModeType | No | Override form mode ("standard" | "conversational") | | screenIndex | number | No | Initial screen index (default 0) | | theme | ThemeConfig | No | Override theme settings | | navigation | NavigationConfig | No | Override navigation settings | | controls | ControlsConfig | No | Override controls settings | | context | Record<string, string \| boolean \| number> | No | External context values for condition evaluation and calculations | | storeRef | MutableRefObject<StoreApi<FBREStoreState> \| null> | No | Ref to access the Zustand store | | onFlowComplete | (data: FlowData) => void | Yes | Called when the user completes the flow | | onScreenChange | (index: number, data: FlowData) => void | No | Called on screen navigation | | onScreenValidationChange | (index: number, data: any) => void | No | Called when screen validity changes |

Server-Driven Mode

Session-based rendering. The server evaluates conditions and validation; the client renders one screen at a time.

| Prop | Type | Required | Description | |------|------|----------|-------------| | sessionEndpoint | string | Yes | Session API base URL | | flowId | string | Yes | ID of the flow to start a session for | | apiKey | string | No | API key for authentication | | mode | FlowModeType | No | Override form mode ("standard" | "conversational") | | theme | ThemeConfig | No | Override theme settings | | context | Record<string, string \| boolean \| number> | No | External context values for condition evaluation and calculations | | onFlowComplete | (data: FlowData) => void | Yes | Called when the user completes the flow | | onScreenChange | (screenNumber: number) => void | No | Called on screen navigation (note: receives screen number, not index + data) |

Imperative Access

Access the store directly for programmatic control:

import { useFBREStore, useFBREStoreApi, useFBREApi } from "@sonata-innovations/fiber-fbre";

// Inside a child of <FBRE />
const flowData = useFBREStore((s) => s.getFlowData());
const storeApi = useFBREStoreApi();
const data = storeApi.getState().getFlowData();

// In remote mode — access API config, flowId, tenantId
const apiContext = useFBREApi();

Events

import { addFBREEventListener, removeFBREEventListener } from "@sonata-innovations/fiber-fbre";

const handler = (id, data) => console.log("file uploaded:", id, data);
addFBREEventListener("file-upload", handler);
removeFBREEventListener("file-upload", handler);

Exports

// Components
import { FBRE } from "@sonata-innovations/fiber-fbre";

// Store hooks
import { useFBREStore, useFBREStoreApi, useFBREApi } from "@sonata-innovations/fiber-fbre";

// Event system
import { addFBREEventListener, removeFBREEventListener } from "@sonata-innovations/fiber-fbre";

// Error classes
import { ApiError, TimeoutError } from "@sonata-innovations/fiber-fbre";

// Types
import type {
  FBREProps,
  FBRELocalProps,
  FBRERemoteProps,
  FBREServerDrivenModeProps,
  FBREStoreState,
  FBREApiConfig,
  Flow,
  FlowScreen,
  FlowConfiguration,
  ThemeConfig,
  NavigationConfig,
  ControlsConfig,
  FlowMetadata,
  Component,
  ComponentProperties,
  ComponentDisplayProperties,
  ComponentDividerProperties,
  ComponentInputProperties,
  ComponentOptionProperties,
  ComponentSliderProperties,
  ComponentSwitchProperties,
  ComponentRatingProperties,
  ComponentFileUploadProperties,
  ComponentGroupProperties,
  FlowData,
  ScreenData,
  ComponentData,
  FileUploadData,
  FileUploadBase64Data,
  FileUploadS3Data,
  FlowConditionConfig,
  ConditionOperator,
  ConditionRule,
  ConditionGroup,
  ConditionDependency,
} from "@sonata-innovations/fiber-fbre";

Flow JSON Schema

Flow
├── uuid: string
├── metadata: { name?, description?, ...}
├── config?: { mode?, theme?: { color?, darkMode?, style? }, navigation?: { transition?, allowInvalidTransition? }, controls?: { show?, layout?, showStepper? }, summary? }
└── screens: FlowScreen[]
    ├── uuid: string
    ├── label?: string
    ├── conditions?: FlowConditionConfig
    └── components: Component[]
        ├── uuid: string
        ├── type: string
        ├── properties: { ... }
        ├── conditions?: FlowConditionConfig
        └── components?: Component[]  (groups and repeaters)

Component Types

| Type | Category | Description | |------|----------|-------------| | header | Display | Heading text | | text | Display | Rich text with markup support | | divider | Display | Horizontal rule with optional label | | callout | Display | Styled alert/info box with variant coloring (info, success, warning, neutral) | | table | Display | Static comparison/data table with optional column highlighting | | inputText | Input | Single-line text field | | inputTextArea | Input | Multi-line text field | | inputNumber | Input | Numeric input (supports decimal restriction and start adornment) | | dropDown | Selection | Native select dropdown | | dropDownMulti | Selection | Multi-select with tag chips | | checkbox | Selection | Checkbox group | | radio | Selection | Radio button group | | toggleSwitch | Selection | On/off toggle | | yesNo | Selection | Two large tappable buttons for binary yes/no | | cardSelect | Selection | Card-based visual selection with image/icon support | | date | Date & Time | Calendar popup date picker | | time | Date & Time | Hour/minute time selector | | dateTime | Date & Time | Combined date + time picker | | dateRange | Date & Time | Two date pickers for start/end | | timeRange | Date & Time | Two time pickers for start/end | | dateTimeRange | Date & Time | Two datetime pickers for start/end | | fileUpload | Interactive | File picker with size validation | | rating | Interactive | Star rating with half-star precision | | slider | Interactive | Range slider with value label | | colorPicker | Interactive | Saturation/hue picker with hex input | | computed | Display | Calculated field with formula-based computed values | | signature | Interactive | Signature pad — draw on canvas, type name, or both | | group | Container | Component container with layout grid, collapsible | | repeater | Container | Iterable component container (add/remove iterations, pre-populated rows) |

Conditions

Components and screens can be conditionally shown/hidden using FlowConditionConfig:

{
  "action": "show",
  "when": {
    "logic": "and",
    "rules": [
      {
        "source": "other-component-uuid",
        "operator": "equals",
        "value": "yes"
      }
    ]
  }
}

Logic: "and" (all rules must match) or "or" (any rule matches).

Context rules: Set sourceType: "context" on a rule to reference an external context value instead of a component. In this case, source is a context key (not a component UUID).

Operators:

| Category | Operators | |----------|-----------| | Equality | equals, notEquals | | String | contains, notContains, startsWith, endsWith | | Presence | isEmpty, isNotEmpty | | Numeric | greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual | | Set (single) | isOneOf, isNotOneOf | | Set (multi) | includesAny, includesAll, includesNone | | Boolean | isTrue, isFalse |

Condition results are stored separately from the Flow JSON in a conditionResults map (keyed by target UUID). Hidden components are excluded from FlowData output.

Validation

Components support a rules-based validation system via properties.validation:

{
  "validation": {
    "rules": [
      { "type": "required" },
      { "type": "email" },
      { "type": "minLength", "params": { "min": 5 } }
    ]
  }
}

17 built-in validators: required, email, phone, url, minLength, maxLength, exactLength, minValue, maxValue, pattern, minSelected, maxSelected, fileType, fileSize, contains, excludes, matchesField

Each validator has a default error message. Custom messages can be set per rule via the message field.

Error display: The first failing message is shown below the field with an "(and N more)" count when multiple rules fail.

Cross-field validation: matchesField enables bidirectional re-evaluation — changing either field re-validates the other.

Migration: Legacy required and regex flat properties are automatically converted to validation rules on load.

Validation errors are stored in validationErrors: Record<string, string[]> on the store (keyed by component UUID). Hidden components (by conditions) do not block screen validity.

Calculations

Flows can include formula-based computed values via FlowCalculation:

{
  "calculations": [
    {
      "uuid": "calc-uuid",
      "formula": "{{comp-a-uuid}} + {{comp-b-uuid}} * 0.1",
      "label": "Total"
    }
  ]
}
  • Formulas reference component values via {uuid} tokens and support standard arithmetic (+, -, *, /)
  • Aggregation functions: SUM(), COUNT(), AVG(), MIN(), MAX() over repeater iterations
  • Scalar MIN(expr, ...) / MAX(expr, ...) for capping values (e.g. MIN({discount}, 25))
  • IF(condition, then, else) for conditional formulas (e.g. tiered pricing)
  • Comparison operators: >, <, >=, <=, ==, != (return 1/0)
  • Option metadata is accessible for option-based components
  • Results update reactively when source values change
  • Access results via getCalculationResult(uuid) on the store

Markup

Text components and detail fields support inline markup:

  • [b]bold[/b]bold
  • [i]italic[/i]italic
  • [l href="url"]link[/l]link

Conversational Mode

Set mode: "conversational" to transform FBRE into a one-question-per-screen experience:

// Via JSX prop
<FBRE flow={myFlow} mode="conversational" onFlowComplete={handleComplete} />

// Via flow config
const flow = {
  ...myFlow,
  config: { ...myFlow.config, mode: "conversational" }
};
<FBRE flow={flow} onFlowComplete={handleComplete} />

What changes:

  • Content is vertically and horizontally centered
  • Single-select components (radio, yesNo, cardSelect, dropDown) auto-advance ~500ms after selection
  • Pressing Enter on inputText / inputNumber advances to the next screen
  • Components animate in with fade + scale + stagger on screen transitions (respects prefers-reduced-motion)
  • Option items, Yes/No buttons, card-select cards, and input fields are enlarged for easier tapping
  • Each of the 6 existing styles has a tailored conversational presentation (e.g. clean = bordered cards, soft-outlined = pill-shaped options, defined-outlined = inverted selection)

What doesn't change:

  • FlowData output is identical
  • Conditions, validation, calculations all work the same
  • Navigation buttons still work alongside auto-advance
  • Works with all 6 transition types and dark mode

Theming

FBRE uses CSS custom properties. Override on the container:

.fbre-container {
  --fbre-theme-color: #1976d2;
  --fbre-theme-light: #e3f2fd;
  --fbre-theme-dark: #1565c0;
  --fbre-error: #d32f2f;
  --fbre-text: #212121;
  --fbre-text-secondary: #666;
  --fbre-border: #ccc;
  --fbre-bg: #fff;
  --fbre-radius: 4px;
  --fbre-font: "Segoe UI", system-ui, sans-serif;
}

theme.color from flow.config.theme.color is applied automatically via inline style.

Dark Mode

Enable dark mode via the theme prop or config:

// Via theme prop (takes precedence)
<FBRE flow={myFlow} theme={{ darkMode: true }} onFlowComplete={handleComplete} />

// Via flow config
const flow = {
  ...myFlow,
  config: { ...myFlow.config, theme: { darkMode: true } }
};
<FBRE flow={flow} onFlowComplete={handleComplete} />

Dark mode applies data-mode="dark" on the container and overrides all CSS custom properties with a dark palette. Custom theme.color values are preserved — the inline style takes precedence over the dark mode default.

Screen Transitions

Animate screen changes by setting navigation.transition in the flow config:

{
  "config": {
    "navigation": { "transition": "slideFade" }
  }
}

| Type | Effect | Direction-aware? | |------|--------|-----------------| | "none" | Instant swap (default) | — | | "slide" | Full horizontal slide | Yes | | "fade" | Crossfade | No | | "slideFade" | 30px slide + opacity | Yes | | "rise" | Vertical rise/sink | Yes | | "scaleFade" | Scale + opacity | No |

Direction-aware transitions reverse when navigating backward. Buttons are disabled during animation to prevent overlapping transitions.

Customize timing via CSS custom properties:

.fbre-container {
  --fbre-transition-duration: 250ms;
  --fbre-transition-easing: cubic-bezier(0.4, 0, 0.2, 1);
}

@media (prefers-reduced-motion: reduce) sets duration to 0ms automatically.

Architecture

| Concern | Implementation | |---------|---------------| | UI framework | Zero-dependency custom CSS | | Styling | CSS custom properties + BEM (fbre- prefix) | | Display detection | isDisplayComponent() checks type against constant set (header, text, divider, callout, table) | | State | Zustand store factory, one store per <FBRE /> instance via React Context | | Re-renders | Zustand selectors — granular, automatic | | Build | Vite library mode | | Components | Registry pattern (Map<string, FC>), one file per type | | Dependencies | react, zustand, @sonata-innovations/fiber-types, @sonata-innovations/fiber-shared |

FlowData Output

getFlowData() returns collected form data. Display-only components (header, text, divider, callout, table) are excluded automatically based on their type.

FlowData
├── uuid: string
├── metadata: Record<string, string>
└── screens: ScreenData[]
    ├── uuid: string
    ├── label?: string
    └── components: ComponentData[]
        ├── uuid: string
        ├── type: string
        ├── label?: string          (omitted when empty)
        ├── value: string | string[] | number | number[] | boolean | null | FileUploadData
        └── components?: ComponentData[][]  (groups and repeaters)

Build

npm run build

Output:

dist/
├── index.d.ts        # Bundled type declarations
├── fiber-fbre.js     # ES module
├── fiber-fbre.cjs    # CommonJS
└── fiber-fbre.css    # All styles

Responsive Sizing

FBRE uses CSS container queries to automatically scale controls in narrow containers (under 400px). Buttons, stepper dots, and toggle switches shrink proportionally. No configuration needed — just embed in a smaller container and the compact sizing kicks in.

Multiple Instances

Each <FBRE /> creates its own isolated Zustand store. Multiple instances can run simultaneously on the same page without state conflicts.

<FBRE flow={flowA} onFlowComplete={handleA} />
<FBRE flow={flowB} onFlowComplete={handleB} />