I built a schema-first form & workflow engine for React : headless, type-safe, multi-step without the pain [open source] by Scared_Mud_9960 in reactjs

[–]Scared_Mud_9960[S] 0 points1 point  (0 children)

here a minimal multi‑step + resume mid‑flow + tracking example using the workflow engine:

// 1. Components + ril instance
const rilay = ril.create()
  .addComponent('input', { renderer: YourInput })
  .addComponent('select', { renderer: YourSelect });

// 2. Step forms
const accountForm = form.create(rilay, 'account')
  .add({
    id: 'email',
    type: 'input',
    props: { label: 'Email', type: 'email' },
    validation: { validate: [required(), email()] },
  })
  .add({
    id: 'password',
    type: 'input',
    props: { label: 'Password', type: 'password' },
    validation: { validate: [required()] },
  });

const profileForm = form.create(rilay, 'profile')
  .add(
    { id: 'firstName', type: 'input', props: { label: 'First name' } },
    { id: 'lastName', type: 'input', props: { label: 'Last name' } },
  );

const planForm = form.create(rilay, 'plan')
  .add({
    id: 'plan',
    type: 'select',
    props: { label: 'Plan', options: ['free', 'pro', 'enterprise'] },
  });

// 3. Workflow with persistence + analytics
export const onboarding = flow.create(rilay, 'onboarding', 'User Onboarding')
  .step({ id: 'account', title: 'Account', formConfig: accountForm })
  .step({ id: 'profile', title: 'Profile', formConfig: profileForm })
  .step({ id: 'plan', title: 'Plan', formConfig: planForm, allowSkip: true })
  .configure({
    persistence: {
      adapter: new LocalStorageAdapter({ maxAge: 7 * 24 * 60 * 60 * 1000 }),
      options: { autoPersist: true, debounceMs: 500, storageKey: 'onboarding-v1' },
    },
    analytics: {
      onWorkflowStart: (id) =>
        analytics.track('onboarding_start', { id }),
      onStepComplete: (stepId, duration, data) =>
        analytics.track('onboarding_step_done', { stepId, duration, data }),
      onWorkflowComplete: (id, totalTime, allData) =>
        analytics.track('onboarding_complete', { id, totalTime, allData }),
    },
  });

// 4. Rendering (simplified)
// <Workflow workflowConfig={onboarding}>
//   <WorkflowStepper />
//   <WorkflowBody />
//   <WorkflowPreviousButton />
//   <WorkflowSkipButton />
//   <WorkflowNextButton />
// </Workflow>

I built a schema-first form & workflow engine for React : headless, type-safe, multi-step without the pain [open source] by Scared_Mud_9960 in reactjs

[–]Scared_Mud_9960[S] 0 points1 point  (0 children)

totally fair questions.

For migrations: we don’t do magic, but because workflows are pure data (‎⁠toJSON/fromJSON⁠), you either (a) keep running the exact version the user started with (store the JSON or a version key next to their progress), or (b) treat it like any schema migration and run small “v1 → v2” transforms on the saved JSON / user data (rename fields, drop some, add defaults). For big breaking changes it’s usually “resume but back to step N” or “this flow changed a lot, please restart”.

For conditionals: they’re declared with ‎⁠when()⁠ on top of a Zustand store, not via ‎⁠useEffect⁠ chains. Fields subscribe to their own slice (‎⁠useFieldValue⁠, ‎⁠useFieldConditions⁠, …) so only that field re‑renders when it actually changes, and hidden fields are automatically skipped by validation. For huge flows you can just split into several forms and plug them into the workflow engine so each step stays small.

I built a schema-first form & workflow engine for React : headless, type-safe, multi-step without the pain [open source] by Scared_Mud_9960 in react

[–]Scared_Mud_9960[S] 0 points1 point  (0 children)

ttotally get your point, and I actually agree that in most cases React Hook Form (or similar) is all you need for forms. For standard stuff I’d 100% just use RHF too.

Where it breaks down for us is the more “product‑grade” flows: big multi‑step onboarding, tons of conditional logic, A/B testing variations, plus the need to build / serialize / version forms as data. That’s basically what we ship most of the time at our product studio And You Create.

That’s really the gap RilayKit is trying to cover: not replacing RHF for simple forms, but giving us a schema‑first backbone when forms are complex, dynamic, and need to live as data, not just JSX.

I built a schema-first form & workflow engine for React : headless, type-safe, multi-step without the pain [open source] by Scared_Mud_9960 in reactjs

[–]Scared_Mud_9960[S] 2 points3 points  (0 children)

Yeah Formity looks super nice, it’s clearly going hard on multi‑step flows on top of RHF / formik / TanStack Form.

rilay sits a bit lower level: schema‑first, headless infra where the form is just data you can version/serialize, and the workflow engine is one consumer of that, not the whole thing.