grill-me
Interview the user relentlessly about a plan or design until reaching shared understanding,…
Building forms with the FormModel system — field types, renderers, layout, validation, conditional rules, computed fields, and dynamic zones. Use this skill when the developer needs to define form fields with the builder API, choose renderers, build layouts with
$ npx -y skills add webiny/webiny-js --skill form-model --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/form-modelContext preview
The summary Claude sees to decide when to auto-load this skill.
Building forms with the FormModel system — field types, renderers, layout, validation, conditional rules, computed fields, and dynamic zones. Use this skill when the developer needs to define form fields with the builder API, choose renderers, build layouts with
name: webiny-form-model description: > Building forms with the FormModel system — field types, renderers, layout, validation, conditional rules, computed fields, and dynamic zones. Use this skill when the developer needs to define form fields with the builder API, choose renderers, build layouts with tabs/rows/separators, add validation (Zod or imperative), use conditional visibility/disable rules, create computed fields, or work with object fields and templates (dynamic zones).
The Form Model is Webiny's declarative form system. Define fields with a fluent builder API (`fields.text()`, `fields.datetime()`, etc.), arrange them with a layout builder (`layout.row()`, `layout.tabs()`, etc.), and validate with Zod schemas or imperative rules. Fields support conditional visibility, computed values, reactive context from other fields (`.context()`), and deeply nested object/list structures with templates (dynamic zones).
All fields are created via the `fields` registry callback. Each builder method returns a chainable builder.
fields.text();
Default renderer: `textInput`. Value: `string | null`.
fields.text().label("Title").placeholder("Enter title").required("Title is required")
fields.text().renderer("textarea", { rows: 4 })
fields.text().list().renderer("tags").defaultValue([])
fields.text().list().renderer("textInputs", { addItemLabel: "Add text" })
fields.text().list().renderer("textareas", { addItemLabel: "Add description" })
fields.text().renderer("codeEditor", { language: "html", height: 300 })
fields.text().options([
{ label: "Option A", value: "a" },
{ label: "Option B", value: "b" }
]) // auto-switches to "select" renderer
fields.text().options([...]).renderer("radioButtons")
fields.text().list().options([...]).renderer("checkboxes")
// Dynamic options — callback receives { field, form }, re-evaluated reactively
fields.text().options(({ form }) => {
const type = form.field("general.type").getValue();
return getOptionsForType(type);
})fields.number();
Default renderer: `numberInput`. Value: `number | null`. Auto-normalizes to number.
fields.number().label("Count").placeholder("0").required();
fields.number().list().renderer("numberInputs", { addItemLabel: "Add number" });
fields.number().options([
{ label: "Tier 1", value: 100 },
{ label: "Tier 2", value: 200 }
]);fields.boolean();
Default renderer: `switch`. Value: `boolean | null`.
fields.boolean().label("Featured").defaultValue(false);fields.datetime();
Default renderer: `dateTimeInput`. Pick a variant method to set the subtype:
| Variant | Value Format | Example | | ----------------------------------------- | ------------------------------ | ----------------------- | | `.dateOnly()` | `"2026-05-01"` | Birthdays, due dates | | `.timeOnly()` | `"14:30:00"` | Opening hours | | `.withTimezone()` | `"2026-05-01T14:30:00+02:00"` | Events tied to a locale | | `.withoutTimezone()` | `"2026-05-01T14:30:00.000Z"` | Timestamps, logs | | `.monthOnly()` | `"2026-05"` | Billing cycles | | `.weekOnly({ startsOn: 1 })` | `"2026-W18"` | Sprint planning | | `.yearOnly({ range: [2020, 2035] })` | `2026` (number) | Fiscal years | | `.dateRange()` | `{ from: "...", to: "..." }` | Vacation requests | | `.multipleDates()` | `["2026-05-01", "2026-05-03"]` | Blackout dates | | `.multipleMonths()` | `["2026-01", "2026-03"]` | Seasonal availability | | `.multipleYears({ range: [2020, 2035] })` | `[2024, 2025, 2026]` | Multi-year budgets |
Additional chainable methods:
.presets([
{ label: "Today", value: () => new Date() },
{ label: "In a week", value: () => addDays(new Date(), 7) }
])
.displayFormat("dd/MM/yyyy") // date-fns format tokens
.list() // switches renderer to "dateTimeInputs"fields.file();
Default renderer: `filePicker`. Value: `FileValue | null` (object with `id`, `name`, `size`, `mimeType`, `src`, `width`, `height`).
fields.file().label("Image");fields.fileUrl();
Default renderer: `fileUrlPicker`. Value: `string | null` (URL only).
fields.fileUrl().label("Image URL");fields.lexical();
Default renderer: `lexical`. Value: `RichTextValueWithHtml | null` (`{ state: string; html: string }`).
fields.lexical().label("Content").required("Content is required");fields.password();
Default renderer: `passwordInput`. Value: `string | null`.
fields.password().label("Password").required("Password is required");fields.permissions();
Default renderer: `permissions`. Value: `Record<string, unknown>[]`. Has a built-in Zod schema requiring at least one permission entry.
fields.permissions().label("Permissions");fields.rolesMultiSelect();
Default renderer: `rolesMultiSelect`. Value: `unknown[]`.
fields.rolesMultiSelect().label("Roles");fields.object();
Default renderer: `objectAccordionSingle`. For nested structures, lists, and dynamic zones.
// Simple nested object
fields.object().label("Address").fields(f => ({
street: f.text().label("Open-source content platform. Self-hosted on AWS serverless. Built as a TypeScript framework you extend with code, not a closed product you configure through a UI. Runs on Lambda, DynamoDB, S3, and CloudFront inside your own AWS account. Scales automatically.
Repo: webiny/webiny-js
Interview the user relentlessly about a plan or design until reaching shared understanding,…
Turn a PRD into a multi-phase implementation plan using tracer-bullet vertical slices, saved…
Webiny-only. Run all checks required before packages are ready for publish: deps, build,…
Use when running tests. Shows how to run tests for a single package, including OpenSearch…
Generate, refresh, and maintain Webiny MCP server skills from source documentation and…
Create a PRD through user interview, codebase exploration, and module design, then submit as…