Skip to main content

webiny-form-model

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).

Informações da origem

Repositório
webiny/webiny-js
Última atividade na origem
20 de outubro de 2026 às 12:01
Idioma detectado do SKILL.md
inglês
Estrelas
8.050
Forks
683

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
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).
# Form Model ## TL;DR 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). ## Field Types All fields are created via the `fields` registry callback. Each builder method returns a chainable builder. ### Text ```typescript fields.text(); ``` Default renderer: `textInput`. Value: `string | null`. ```typescript 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); }) ``` ### Number ```typescript fields.number(); ``` Default renderer: `numberInput`. Value: `number | null`. Auto-normalizes to number. ```typescript 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 } ]); ``` ### Boolean ```typescript fields.boolean(); ``` Default renderer: `switch`. Value: `boolean | null`. ```typescript fields.boolean().label("Featured").defaultValue(false); ``` ### DateTime ```typescript fields.datetime(); ``` Default renderer: `dateTimeInput`. Pick a variant method to set the subtype: | Variant | Value Format | Example | | ----------------------------------------- | ------------------------------ | -------------------------------------------------------------- | | `.dateOnly()` | `"2026-05-01"` | Holidays, due dates (birthdays: see the birthday picker below) | | `.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: ```typescript .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" .dateGte("2026-01-01", "message?") // value must be on or after .dateLte({ expression: "today-18y" }, "message?") // value must be on or before ``` `.dateGte()` and `.dateLte()` take a fixed value, or, on a date-only field, `{ expression }` worked out from the editor's local date every time the field is checked. An expression is `today`, or `today` plus or minus a number of days (`d`), months (`m`) or years (`y`): `"today-18y"`, `"today+1d"`. The limits survive a later `.renderer()` or `.schema()` call, and the check runs after the field's own schema. In the message, `{date}` is the limit in the admin's date format; without a message, the field says "Must be on or after {date}." or "Must be on or before {date}.". For dates far in the past, like a date of birth, a date-only field can use the `birthdayInput` renderer, a step-by-step picker (day, then month, then year). It stores the same `"YYYY-MM-DD"` value as the calendar, and has no settings: it offers only the dates the field's `dateGte`/ `dateLte` limits allow (without limits, this year back 120 years). Chain `.renderer()` after the subtype method, since subtype methods reset the renderer: ```typescript fields.datetime().dateOnly().renderer("birthdayInput"); fields .datetime() .dateOnly() .dateGte({ expression: "today-100y" }) // not older than 100 .dateLte({ expression: "today-18y" }, "You must be 18 or older.") .renderer("birthdayInput"); ``` ### File ```typescript fields.file(); ``` Default renderer: `filePicker`. Value: `FileValue | null` (object with `id`, `name`, `size`, `mimeType`, `src`, `width`, `height`). ```typescript fields.file().label("Image"); ``` ### File URL ```typescript fields.fileUrl(); ``` Default renderer: `fileUrlPicker`. Value: `string | null` (URL only). ```typescript fields.fileUrl().label("Image URL"); ``` ### Lexical ```typescript fields.lexical(); ``` Default renderer: `lexical`. Value: `RichTextValueWithHtml | null` (`{ state: string; html: string }`). ```typescript fields.lexical().label("Content").required("Content is required"); ``` ### Password ```typescript fields.password(); ``` Default renderer: `passwordInput`. Value: `string | null`. ```typescript fields.password().label("Password").required("Password is required"); ``` ### Permissions ```typescript fields.permissions(); ``` Default renderer: `permissions`. Value: `Record<string, unknown>[]`. Has a built-in Zod schema requiring at least one permission entry. ```typescript fields.permissions().label("Permissions"); ``` ### Roles Multi-Select ```typescript fields.rolesMultiSelect(); ``` Default renderer: `rolesMultiSelect`. Value: `unknown[]`. ```typescript fields.rolesMultiSelect().label("Roles"); ``` ### Object ```typescript fields.object(); ``` Default renderer: `objectAccordionSingle`. For nested structures, lists, and dynamic zones. ```typescript // Simple nested object fields.object().label("Address").fields(f => ({ street: f.text().label("Street"), city: f.text().label("City"), zip: f.text().label("ZIP") })) // List of objects fields.object().list().label("Authors").fields(f => ({ name: f.text().label("Name").required(), email: f.text().label("Email") })) // Dynamic zone (single template selection) fields.object().label("Content Block") .template("hero", t => { t.label("Hero Banner") .icon({ type: "icon", name: "fas/image" }) .fields(f => ({ heading: f.text().label("Heading").required(), image: f.file().label("Image") })); }) .template("text", t => { t.label("Rich Text").fields(f => ({ body: f.text().label("Body").renderer("textarea") })); }) // Dynamic zone list (multiple items, each picks a template) fields.object().list().label("Page Sections") .renderer("dynamicZone", { container: false }) .template("hero", t => { ... }) .template("cta", t => { ... }) // Key-value list fields.object().list().label("Meta Tags") .renderer("keyValueTags", { addItemLabel: "Add tag" }) .fields(f => ({ name: f.text().placeholder("Name"), content: f.text().placeholder("Content") })) ``` Template visibility can be conditional: ```typescript .template("premium", t => { t.label("Premium Widget") .visible(form => form.field("plan").getValue() === "enterprise") .fields(f => ({ ... })); }) ``` ## Common Builder Methods These are available on **all** field types: | Method | Description |
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub