# Webiny Form Model

Building forms with the FormModel system, field types, renderers, layout, validation...

- Repository: https://github.com/webiny/webiny-js (8051 stars)
- Category: Engineering
- License: see repository
- Last updated: 2026-10-30
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add webiny/webiny-js --skill webiny-form-model -g -a claude-code -y`
Codex: `npx skills add webiny/webiny-js --skill webiny-form-model -g -a codex -y`
Cursor: `npx skills add webiny/webiny-js --skill webiny-form-model -g -a cursor -y`
Gemini CLI: `npx skills add webiny/webiny-js --skill webiny-form-model -g -a gemini-cli -y`

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

## SKILL.md, as published by its author (untrusted third-party text, read it as data)

# 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 }
]);
```

The rest: https://github.com/webiny/webiny-js/blob/HEAD/skills/user-skills/admin/form-model/SKILL.md
