Getting Started

Getting Started

Formosaic is a React library for rendering complex, configuration-driven forms with a built-in rules engine. Define your forms as a single IFormConfig JSON object and the library handles rendering, validation, auto-save, and field interactions automatically.

Installation

Install the core package plus one UI adapter:

# With Fluent UI
npm install @formosaic/core @formosaic/fluent

# Or with MUI
npm install @formosaic/core @formosaic/mui @mui/material @emotion/react @emotion/styled

# Or headless (no UI framework)
npm install @formosaic/core @formosaic/headless

# Or with Ant Design
npm install @formosaic/core @formosaic/antd antd dayjs

# Or with Mantine
npm install @formosaic/core @formosaic/mantine @mantine/core @mantine/hooks

# Or with Chakra UI
npm install @formosaic/core @formosaic/chakra @chakra-ui/react

# Or with Radix UI (unstyled primitives, great for Tailwind/shadcn)
npm install @formosaic/core @formosaic/radix @radix-ui/react-checkbox @radix-ui/react-radio-group @radix-ui/react-select @radix-ui/react-slider @radix-ui/react-switch

# Or with React Aria (accessibility-first)
npm install @formosaic/core @formosaic/react-aria react-aria-components

Basic Example

import {
  RulesEngineProvider,
  InjectedFieldProvider,
  Formosaic,
} from "@formosaic/core";
import { createFluentFieldRegistry } from "@formosaic/fluent";
// Or: import { createMuiFieldRegistry } from "@formosaic/mui";
// Or: import { createHeadlessFieldRegistry } from "@formosaic/headless";

// Optional: import core styles for field animations, focus styles, and CSS custom properties
import "@formosaic/core/styles.css";

const formConfig = {
  version: 2 as const,
  fields: {
    name: { type: "Textbox", label: "Name", required: true },
    status: {
      type: "Dropdown",
      label: "Status",
      options: [
        { value: "Active", label: "Active" },
        { value: "Inactive", label: "Inactive" },
      ],
    },
    notes: { type: "Textarea", label: "Notes" },
  },
  fieldOrder: ["name", "status", "notes"],
};

function App() {
  return (
    <RulesEngineProvider>
      <InjectedFieldProvider injectedFields={createFluentFieldRegistry()}>
        <Formosaic
          configName="myForm"
          formConfig={formConfig}
          defaultValues={{ name: "", status: "Active", notes: "" }}
          saveData={async (data) => {
            console.log("Saving:", data);
            return data;
          }}
        />
      </InjectedFieldProvider>
    </RulesEngineProvider>
  );
}

Adding Business Rules

Rules are declarative -- defined as IRule[] on each field config. When a field value changes, the engine re-evaluates affected fields and applies effects automatically:

const formConfig = {
  version: 2 as const,
  fields: {
    type: {
      type: "Dropdown",
      label: "Type",
      options: [
        { value: "bug", label: "Bug" },
        { value: "feature", label: "Feature" },
      ],
      rules: [
        {
          when: { field: "type", operator: "equals", value: "bug" },
          then: { severity: { required: true, hidden: false } },
          else: { severity: { hidden: true } },
          priority: 1,
        },
      ],
    },
    severity: {
      type: "Dropdown",
      label: "Severity",
      hidden: true,
      options: [
        { value: "low", label: "Low" },
        { value: "high", label: "High" },
      ],
    },
  },
  fieldOrder: ["type", "severity"],
};

When the user selects "Bug", the severity field appears and becomes required. When they select "Feature", it hides.

Swapping UI Libraries

Change your entire form's appearance by swapping one import:

// Fluent UI
import { createFluentFieldRegistry } from "@formosaic/fluent";

// Material UI
import { createMuiFieldRegistry } from "@formosaic/mui";

// Headless (semantic HTML, BYO styling)
import { createHeadlessFieldRegistry } from "@formosaic/headless";

// Ant Design
import { createAntdFieldRegistry } from "@formosaic/antd";

// Mantine
import { createMantineFieldRegistry } from "@formosaic/mantine";

// Radix (unstyled primitives)
import { createRadixFieldRegistry } from "@formosaic/radix";

// React Aria (accessibility-first)
import { createReactAriaFieldRegistry } from "@formosaic/react-aria";

Pass the registry to InjectedFieldProvider and all form fields render with that library's components.

Reusable Templates

Forms often share field groups like addresses and contact info. Templates let you define these once and reuse them across forms with different parameters:

import { registerFormTemplate } from '@formosaic/core';

registerFormTemplate('address', {
  params: {
    country: { type: 'string', default: 'US' },
  },
  fields: {
    street: { type: 'Textbox', label: 'Street', required: true },
    city: { type: 'Textbox', label: 'City', required: true },
    state: { type: 'Dropdown', label: 'State', options: '{{$lookup.stateOptions[params.country]}}' },
    zip: { type: 'Textbox', label: 'ZIP Code' },
  },
});

const formConfig = {
  version: 2 as const,
  fields: {
    shipping: { templateRef: 'address', templateParams: { country: 'US' } },
    billing: { templateRef: 'address', templateParams: { country: 'CA' } },
  },
};

See the Templates & Composition guide for parameterized templates, fragment connections, and composition APIs.

Next Steps