rilaykit

📁 andyoucreate/rilaykit 📅 6 days ago
8
总安装量
8
周安装量
#35876
全站排名
安装命令
npx skills add https://github.com/andyoucreate/rilaykit --skill rilaykit

Agent 安装分布

openclaw 8
gemini-cli 8
github-copilot 8
codex 8
kimi-cli 8
cursor 8

Skill 文档

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.

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)

Core Workflow

1. Create a ril instance and register components

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:

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

2. Build forms with the fluent builder

import { form } from "@rilaykit/forms";

const loginForm = form
  .create(r, "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.

3. Render forms headlessly

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

import { flow } from "@rilaykit/workflow";

const onboarding = flow
  .create(r, "onboarding", "User Onboarding")
  .step({ id: "personal", title: "Personal Info", formConfig: personalForm })
  .step({
    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

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

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,
}

Conditions: Fluent builder with logical operators

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

Granular hooks: Subscribe only to what you need

// 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();

Reusable step definitions

import { form } from "@rilaykit/forms";

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

// Conditionally add steps
if (!hasExistingClient) {
  workflowFlow = workflowFlow.step(personalInfoStep(t));
}

Critical Rules

  • Immutable builders: Every .add(), .step(), .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.

Detailed API References