rilaykit

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

RilayKit

RilayKit

RilayKit is a headless React framework for dynamic forms and multi-step workflows. It uses an immutable builder pattern, Standard Schema validation, and Zustand-powered granular state hooks.
RilayKit是一个用于构建动态表单和多步骤工作流的无头React框架。它采用不可变构建器模式、标准Schema验证和基于Zustand的细粒度状态钩子。

Architecture Overview

架构概述

Three packages, layered dependency:
@rilaykit/core      → Foundation: ril instance, component registry, validation, conditions
@rilaykit/forms     → Form builder, components, hooks (depends on core)
@rilaykit/workflow   → Flow builder, navigation, persistence, analytics (depends on core + forms)
三个包,分层依赖:
@rilaykit/core      → 基础层:ril实例、组件注册中心、验证、条件判断
@rilaykit/forms     → 表单构建器、组件、钩子(依赖core)
@rilaykit/workflow   → 流程构建器、导航、持久化、分析(依赖core + forms)

Core Workflow

核心工作流

1. Create a ril instance and register components

1. 创建ril实例并注册组件

typescript
import { ril } from "@rilaykit/core";

export const r = ril
  .create()
  .addComponent("input", {
    name: "Text Input",
    renderer: InputRenderer,
    defaultProps: { placeholder: "Enter..." },
  })
  .addComponent("select", {
    name: "Select",
    renderer: SelectRenderer,
    validation: { validate: z.string().optional() },
  })
  .configure({
    rowRenderer: RowRenderer,
    bodyRenderer: BodyRenderer,
    fieldRenderer: FieldRenderer,
    submitButtonRenderer: SubmitButtonRenderer,
    stepperRenderer: StepperRenderer,
    nextButtonRenderer: NextButtonRenderer,
    previousButtonRenderer: PreviousButtonRenderer,
  });
Every component renderer follows the same interface:
typescript
import type { ComponentRenderProps } from "@rilaykit/core";

type ComponentRenderer<T = any> = (props: ComponentRenderProps<T>) => React.ReactElement;

// Props received by every renderer:
// id, value, onChange, onBlur, props, error, disabled, context
typescript
import { ril } from "@rilaykit/core";

export const r = ril
  .create()
  .addComponent("input", {
    name: "Text Input",
    renderer: InputRenderer,
    defaultProps: { placeholder: "Enter..." },
  })
  .addComponent("select", {
    name: "Select",
    renderer: SelectRenderer,
    validation: { validate: z.string().optional() },
  })
  .configure({
    rowRenderer: RowRenderer,
    bodyRenderer: BodyRenderer,
    fieldRenderer: FieldRenderer,
    submitButtonRenderer: SubmitButtonRenderer,
    stepperRenderer: StepperRenderer,
    nextButtonRenderer: NextButtonRenderer,
    previousButtonRenderer: PreviousButtonRenderer,
  });
每个组件渲染器都遵循相同的接口:
typescript
import type { ComponentRenderProps } from "@rilaykit/core";

type ComponentRenderer<T = any> = (props: ComponentRenderProps<T>) => React.ReactElement;

// 每个渲染器接收的Props:
// id, value, onChange, onBlur, props, error, disabled, context

2. Build forms with the fluent builder

2. 使用流畅式构建器创建表单

typescript
const loginForm = r
  .form("login")
  .add(
    { id: "email", type: "input", props: { label: "Email" }, validation: { validate: [required(), email()] } },
    { id: "password", type: "input", props: { type: "password" }, validation: { validate: [required()] } },
  );
  • Fields passed to the same
    .add()
    call render on the same row (max 3 per row).
  • Separate
    .add()
    calls create separate rows.
typescript
const loginForm = r
  .form("login")
  .add(
    { id: "email", type: "input", props: { label: "Email" }, validation: { validate: [required(), email()] } },
    { id: "password", type: "input", props: { type: "password" }, validation: { validate: [required()] } },
  );
  • 传递给同一个
    .add()
    调用的字段会渲染在同一行(每行最多3个)。
  • 分开的
    .add()
    调用会创建独立的行

3. Render forms headlessly

3. 无头渲染表单

typescript
import { Form, FormBody, FormSubmitButton } from "@rilaykit/forms";

<Form formConfig={loginForm} onSubmit={handleSubmit} defaultValues={{ email: "" }}>
  <FormBody />
  <FormSubmitButton>Sign In</FormSubmitButton>
</Form>
typescript
import { Form, FormBody, FormSubmitButton } from "@rilaykit/forms";

<Form formConfig={loginForm} onSubmit={handleSubmit} defaultValues={{ email: "" }}>
  <FormBody />
  <FormSubmitButton>Sign In</FormSubmitButton>
</Form>

4. Build multi-step workflows

4. 构建多步骤工作流

typescript
const onboarding = r
  .flow("onboarding", "User Onboarding")
  .addStep({ id: "personal", title: "Personal Info", formConfig: personalForm })
  .addStep({
    id: "company",
    title: "Company",
    formConfig: companyForm,
    conditions: { visible: when("personal.userType").equals("business") },
    onAfterValidation: async (stepData, helper) => {
      const result = await fetchCompany(stepData.siren);
      helper.setNextStepFields({ company: result.name });
    },
  })
  .configure({ analytics: myAnalytics })
  .build();
typescript
const onboarding = r
  .flow("onboarding", "User Onboarding")
  .addStep({ id: "personal", title: "Personal Info", formConfig: personalForm })
  .addStep({
    id: "company",
    title: "Company",
    formConfig: companyForm,
    conditions: { visible: when("personal.userType").equals("business") },
    onAfterValidation: async (stepData, helper) => {
      const result = await fetchCompany(stepData.siren);
      helper.setNextStepFields({ company: result.name });
    },
  })
  .configure({ analytics: myAnalytics })
  .build();

5. Render workflows

5. 渲染工作流

typescript
import { Workflow, WorkflowStepper, WorkflowBody, WorkflowNextButton, WorkflowPreviousButton } from "@rilaykit/workflow";

<Workflow workflowConfig={onboarding} onWorkflowComplete={handleComplete} defaultValues={defaults}>
  <WorkflowStepper />
  <WorkflowBody />
  <div className="flex justify-between">
    <WorkflowPreviousButton />
    <WorkflowNextButton>{(p) => p.isLastStep ? "Complete" : "Next"}</WorkflowNextButton>
  </div>
</Workflow>
typescript
import { Workflow, WorkflowStepper, WorkflowBody, WorkflowNextButton, WorkflowPreviousButton } from "@rilaykit/workflow";

<Workflow workflowConfig={onboarding} onWorkflowComplete={handleComplete} defaultValues={defaults}>
  <WorkflowStepper />
  <WorkflowBody />
  <div className="flex justify-between">
    <WorkflowPreviousButton />
    <WorkflowNextButton>{(p) => p.isLastStep ? "Complete" : "Next"}</WorkflowNextButton>
  </div>
</Workflow>

Key Patterns

核心模式

Validation: Mix libraries freely via Standard Schema

验证:通过Standard Schema自由混合各类库

typescript
import { required, email, pattern, custom, async as asyncValidator } from "@rilaykit/core";
import { z } from "zod";

validation: {
  validate: [
    required("Required"),           // RilayKit built-in
    z.string().email("Invalid"),    // Zod
    asyncValidator(checkEmail, "Already exists"),  // Async
  ],
  validateOnBlur: true,
  debounceMs: 200,
}
typescript
import { required, email, pattern, custom, async as asyncValidator } from "@rilaykit/core";
import { z } from "zod";

validation: {
  validate: [
    required("Required"),           // RilayKit内置
    z.string().email("Invalid"),    // Zod
    asyncValidator(checkEmail, "Already exists"),  // 异步验证
  ],
  validateOnBlur: true,
  debounceMs: 200,
}

Conditions: Fluent builder with logical operators

条件判断:带逻辑运算符的流畅式构建器

typescript
import { when } from "@rilaykit/core";

// Field-level conditions
conditions: {
  visible: when("accountType").equals("business"),
  required: when("accountType").equals("business"),
  disabled: when("status").equals("locked"),
}

// Combine with and/or
when("type").equals("premium")
  .and(when("status").in(["active", "verified"]))
  .or(when("age").greaterThan(65))

// Operators: equals, notEquals, greaterThan, lessThan, contains, notContains,
//            matches, in, notIn, exists, notExists
typescript
import { when } from "@rilaykit/core";

// 字段级条件
conditions: {
  visible: when("accountType").equals("business"),
  required: when("accountType").equals("business"),
  disabled: when("status").equals("locked"),
}

// 使用and/or组合条件
when("type").equals("premium")
  .and(when("status").in(["active", "verified"]))
  .or(when("age").greaterThan(65))

// 支持的运算符:equals、notEquals、greaterThan、lessThan、contains、notContains、
//            matches、in、notIn、exists、notExists

Granular hooks: Subscribe only to what you need

细粒度钩子:仅订阅你需要的内容

typescript
// Field-level (only re-renders when that field changes)
const email = useFieldValue<string>("email");
const errors = useFieldErrors("email");
const { setValue } = useFieldActions("email");

// Form-level
const isSubmitting = useFormSubmitting();
const allValues = useFormValues();
const { reset } = useFormActions();
typescript
// 字段级(仅当该字段变化时才重新渲染)
const email = useFieldValue<string>("email");
const errors = useFieldErrors("email");
const { setValue } = useFieldActions("email");

// 表单级
const isSubmitting = useFormSubmitting();
const allValues = useFormValues();
const { reset } = useFormActions();

Reusable step definitions

可复用的步骤定义

typescript
// Define once, use across multiple flows
export const personalInfoStep = (t: TranslationFn): StepDefinition => ({
  id: "personalInfo",
  title: t("steps.personalInfo.title"),
  formConfig: form.create(r).add(/* fields */),
});

// Conditionally add steps
if (!hasExistingClient) {
  workflowFlow = workflowFlow.addStep(personalInfoStep(t));
}
typescript
// 定义一次,在多个流程中复用
export const personalInfoStep = (t: TranslationFn): StepDefinition => ({
  id: "personalInfo",
  title: t("steps.personalInfo.title"),
  formConfig: form.create(r).add(/* 字段 */),
});

// 条件式添加步骤
if (!hasExistingClient) {
  workflowFlow = workflowFlow.addStep(personalInfoStep(t));
}

Critical Rules

重要规则

  • Immutable builders: Every
    .add()
    ,
    .addStep()
    ,
    .configure()
    returns a new instance. Chain calls.
  • Headless architecture: You provide ALL renderers. RilayKit handles state, validation, conditions, navigation.
  • One ril instance per app: Register all components and renderers once, reuse everywhere.
  • Granular hooks over useFormConfigContext: Prefer
    useFieldValue
    ,
    useFormSubmitting
    etc. to avoid unnecessary re-renders.
  • Form data is namespaced by step ID in workflows: Access via
    data.stepId.fieldId
    .
  • Always call
    .build()
    on workflow configs before passing to
    <Workflow>
    . Form configs auto-build.
  • 不可变构建器:每次调用
    .add()
    .addStep()
    .configure()
    都会返回一个新实例,请链式调用。
  • 无头架构:所有渲染器都由你提供。RilayKit负责状态管理、验证、条件判断和导航。
  • 每个应用一个ril实例:一次性注册所有组件和渲染器,在各处复用。
  • 优先使用细粒度钩子而非useFormConfigContext:推荐使用
    useFieldValue
    useFormSubmitting
    等钩子,避免不必要的重新渲染。
  • 工作流中的表单数据按步骤ID命名空间划分:通过
    data.stepId.fieldId
    访问。
  • 工作流配置在传入<Workflow>前务必调用.build():表单配置会自动构建。

Detailed API References

详细API参考

  • Core (ril instance, validation, conditions): See references/core.md
  • Forms (builder, components, hooks): See references/forms.md
  • Workflow (flow builder, navigation, persistence, analytics): See references/workflow.md
  • Core(ril实例、验证、条件判断):查看references/core.md
  • Forms(构建器、组件、钩子):查看references/forms.md
  • Workflow(流程构建器、导航、持久化、分析):查看references/workflow.md