rilaykit
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,useFormSubmittingetc. 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
- 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