.codex/skills/rezi-forms/SKILL.md
Set up form input handling with validation and submission. Use when building forms with inputs, selects, checkboxes, etc.
npx skillsauth add rtlzeromemory/rezi rezi-formsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill when:
packages/core/src/forms/useForm.ts — useForm() hookpackages/core/src/widgets/types.ts — InputProps, FieldProps, CheckboxProps, SelectPropspackages/core/src/widgets/ui.ts — ui.input(), ui.field(), ui.checkbox(), ui.select()packages/core/src/ui/recipes.ts — recipe.input(), recipe.button(), recipe.select() for design-system stylingUse the useForm() hook inside a defineWidget:
import { defineWidget, ui, useForm } from "@rezi-ui/core";
const MyForm = defineWidget<{ onSubmit: (v: Values) => void }>((props, ctx) => {
const form = useForm(ctx, {
initialValues: { name: "", email: "" },
validate: (values) => {
const errors: Record<string, string> = {};
if (!values.name) errors.name = "Required";
if (!values.email.includes("@")) errors.email = "Invalid email";
return errors;
},
onSubmit: async (values) => props.onSubmit(values),
});
return ui.column({ gap: 1 }, [
ui.field({
label: "Name",
error: form.errors.name,
children: ui.input({ id: "field-name", ...form.bind("name") }),
}),
ui.field({
label: "Email",
error: form.errors.email,
children: ui.input({ id: "field-email", ...form.bind("email") }),
}),
ui.actions([
ui.button({ id: "cancel", label: "Cancel", intent: "secondary", onPress: () => {} }),
ui.button({ id: "save", label: "Save", intent: "primary", onPress: form.handleSubmit }),
]),
]);
}, { name: "MyForm" });
Bind fields using form.bind("fieldName") — returns object-form input props (value, onInput, onBlur, disabled)
Use ui.field() to wrap inputs with labels and error display
Access form state via form.errors, form.touched, form.dirty, form.submitting
Use success/warning for non-submit side actions:
ui.button({ id: "approve", label: "Approve", intent: "success", onPress: handleApprove });
ui.button({ id: "review", label: "Needs Review", intent: "warning", onPress: handleReview });
Inputs are recipe-styled by default when semantic color tokens are available (for example via a ThemeDefinition preset).
Manual overrides merge on top via the same mergeTextStyle(baseStyle, ownStyle) pattern used by the renderer.
Use manual style only for targeted overrides (merged on top of recipe output).
For button visuals, use intent (primary, secondary, danger, success, warning, link) in app code.
Do not use dsVariant/dsTone in app-level form code.
onSubmittesting
Write tests for Rezi widgets, screens, or app logic using createTestRenderer and node:test.
development
Add routing with guards and nested outlets to a Rezi app. Use when building multi-page/screen TUI applications.
tools
Profile and optimize Rezi app performance. Use when app feels slow, frames drop, or render phases take too long.
tools
Add modal dialogs and overlay UI with focus trapping. Use when implementing confirmations, alerts, or layered interfaces.