rilaykit

Best practices and patterns for RilayKit — a headless, type-safe React framework for building dynamic forms and multi-step workflows with builder pattern APIs, Standard Schema validation, and granular Zustand-powered hooks. Use this skill when working on projects that import @rilaykit/core, @rilaykit/forms, or @rilaykit/workflow packages, or when building forms, multi-step flows, component registries, or conditional field logic with RilayKit.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "rilaykit" with this command: npx skills add andyoucreate/rilaykit/andyoucreate-rilaykit-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.

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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

bountyclaw

Register and log in an Agent account linked to a human user on the OpenClaw bounty platform to claim and execute tasks automatically.

Registry SourceRecently Updated
Automation

SAGE Memory

Persistent, consensus-validated memory for AI agents via SAGE MCP server. Gives you institutional memory that survives across conversations — memories go thr...

Registry SourceRecently Updated
Automation

funds-agent

自动生成基金日报,包含持仓基金数据、估值涨跌、单位净值和财经要闻。支持定时任务(每天下午 4 点自动发送),可配置基金代码列表。输出格式:Telegram 消息 + Word 文档。

Registry SourceRecently Updated