Visitar URL original
[6.x] Add Form builder by riasvdv · Pull Request #19384 · craftcms/cms · GitHub
Skip to content

[6.x] Add Form builder - #19384

Merged
brandonkelly merged 66 commits into
6.xfrom
feature/forms
Aug 10, 2026
Merged

brandonkelly merged 66 commits into
6.xfrom
feature/forms

Conversation

@riasvdv

@riasvdv riasvdv commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds a renderer-neutral Form system with Vue and server-rendered HTML renderers.

The main goal is to have all Form Controls be powered by the web components, that way the Vue control and HTML component can both use the same web component internally.

Initial docs: https://github.com/craftcms/cms/blob/feature/forms/docs/forms.md

Form
└── Nodes
    ├── Tab
    │   └── Field (label, instructions, ...)
    │       └── Control (Text, Select, ...)
    ├── Heading
    └── Group
        └── Field
            └── Control

Example: configurable components can replace getSettingsHtml() with a renderer-neutral form definition:

public function settingsForm(FormContext $context = new FormContext): ?Form
{
    return Form::make([
        Field::make(
            label: t('API key', category: 'my-plugin'),
            control: Text::make('apiKey')->value($this->apiKey),
        ),
    ]);
}

Field layouts

Field layouts are compiled into a Form tree: layout tabs and elements become Nodes, while each custom field supplies only its editing Control.

The compiler wraps that control in a Field Node and applies the layout-owned metadata: label, instructions, width, required state, conditions, and identity.

Field settings

This also replaces all core field settings with the new Form system, for example PlainText:

public function settingsForm(FormContext $context = new FormContext): Form
{
    return Form::make([
        Field::make(t('UI Mode'))
            ->instructions(t('How the field should be presented in the control panel.'))
            ->control(Choice::make('uiMode')->value($this->uiMode)->options([
                ['label' => t('Normal'), 'value' => 'normal'],
                ['label' => t('Enlarged'), 'value' => 'enlarged'],
            ])),
        Field::make(t('Placeholder Text'))
            ->instructions(t('The text that will be shown if the field doesn’t have a value.'))
            ->control(Text::make(['placeholder'])->value($this->placeholder)),
        Group::make('plain-text-field-limit', [
            Field::make(t('Maximum'))
                ->instructions(t('The maximum number of characters or bytes the field is allowed to have.'))
                ->control(Number::make('fieldLimit')
                    ->value($this->charLimit ?? $this->byteLimit)
                    ->deltaGroupAtNamespace()
                    ->min(1)),
            Field::make(t('Unit'))
                ->control(Choice::make(['limitUnit'])
                    ->value($this->byteLimit ? 'bytes' : 'chars')
                    ->deltaGroupAtNamespace()
                    ->options([
                        ['label' => t('Characters'), 'value' => 'chars'],
                        ['label' => t('Bytes'), 'value' => 'bytes'],
                    ])),
        ])->label(t('Field Limit')),
        Group::make('plain-text-behavior', [
            Field::make(t('Use a monospaced font'))->control(Lightswitch::make('code')->value($this->code)),
            Field::make(t('Allow line breaks'))->control(Lightswitch::make(['multiline'])->value($this->multiline)),
        ]),
        Field::make(t('Initial Rows'))
            ->control(Number::make('initialRows')
                ->value($this->initialRows)
                ->min(1)),
    ]);
}

Kitchen sink

This PR also adds a full kitchen sink to the workbench, which is similar to the Storybook but allows us to test the form nodes and controls within the control panel for both Vue and HTML renderers

image

@github-actions

github-actions Bot commented Aug 10, 2026 •

Copy link
Copy Markdown

📚 Storybook previews

@craftcms/ui — open Storybook

Changed components:

resources/js — open Storybook

No changed components detected in this Storybook.

@riasvdv
riasvdv requested a balanced review from Copilot August 10, 2026 08:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

@riasvdv
riasvdv marked this pull request as ready for review August 10, 2026 10:40
@riasvdv riasvdv changed the title [6.x] Add renderer-neutral Form system [6.x] Add Form builder Aug 10, 2026
@brandonkelly
brandonkelly merged commit ae5462c into 6.x Aug 10, 2026
23 checks passed
@riasvdv
riasvdv deleted the feature/forms branch September 5, 2026 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants