Define form elements from SDC [#3508641] | Drupal.org
Skip to search
Can we use first and third party cookies and web beacons to
understand our audience, and to tailor promotions you see
Define form elements from SDC
Patch (to be ported)
Project:
Drupal core
Version:
11.x-dev
Component:
single-directory components
Priority:
Normal
Category:
Feature request
Assigned:
Unassigned
Issue tags:
ddd2025
Chicago2026
DevDaysAthens2026
Reporter:
pdureau
Created:
24 Feb 2025 at 10:38 UTC
Updated:
24 Apr 2026 at 12:37 UTC
Jump to comment:
Most recent
Most recent file
Problem/Motivation
In
#3494634: Compatibility between SDC and the Form API
, we discussed 2 incompatibilities between SDC and the form API:
we can put a full form into a component slot, but we can't put a form element of a form defined outside the component
we can't define form element, directly usable with the Form API, with SDC. So we can't easily implement some design systems components like
Let's deal with the second point in this dedicated ticket.
Proposed resolution
Form properties to evaluate:
#ajax: (array) Array of elements to specify Ajax behavior. See the Javascript API and AJAX Forms guides for more information.
#default_value: Default value for the element. See also #value.
#description: (string) Help or description text for the element. In an ideal user interface, the #title should be enough to describe the element, so most elements should not have a description; if you do need one, make sure it is translated. If it is not already wrapped in a safe markup object, it will be filtered for XSS safety.
#description_display: (string) Where and how to display the #description.
#disabled: (bool) If TRUE, the element is shown but does not accept user input.
#prefix: (string) Prefix to display before the HTML input element. Should be translated, normally. If it is not already wrapped in a safe markup object, will be filtered for XSS safety.
#suffix: (string) Suffix to display after the HTML input element. Should be translated, normally. If it is not already wrapped in a safe markup object, will be filtered for XSS safety.
#required: (bool) Whether or not input is required on the element.
#required_error: (string) Override default error message "@field_title is required" will be used if this is undefined.
#title: (string) Title of the form element. Should be translated.
#title_display: (string) Where and how to display the #title.
#value: Used to set values that cannot be edited by the user
The other ones are internal, or not self-contained, or needs PHP. That means form elements created from SDC doesn't have:
#after_build: (array) Array of callables or function names, which are called after the element is built. Arguments: $element, $form_state.
#element_validate: (array) Array of callables or function names, which are called to validate the input. Arguments: $element, $form_state, $form.
#process: (array) Array of callables or function names, which are called during form building. Arguments: $element, $form_state, $form.
#value_callback: (callable) Callable or function name, which is called to transform the raw user input to the element's value. Arguments: $element, $input, $form_state.
...
This will not be a missing feature, but a welcomed feature, however:
If really needed by the front developer, to implement UI logic, we can imagine so declarative ways:
inline twig transformation for
#process
json schema for
#element_validate
...
If a back developer want to add some callbacks, it will be a applicative/business need. The form elements that will be created will be alterable like any other form elements.
So, it will be the opportunity to split UI logic from business logic, which is not currently the case in the Form API.
How do we create form elements (which are plugins too) from those SDC plugins? Plugin derivatives?
Remaining tasks
Let's start by trying to re-implement a few Core form element with SDC, some simple, some complex:
Textfield
Checkboxes
Checkbox
Actions
Submit button
...
Out of scope
The scope of this ticket is not to override or replace existing form element but to create new form elements, aside the existing ones, from SDC. We hope one day all Core form elements will be defined as SDC components, but it will be other issues.
Also, we don't address the need of derivative configurable plugins like Field Widgets or WebformElement. It may be the purpose of some contrib modules.
Comment
File
Size
Author
#37
3508641-nr-bot_cvdym73b.txt
2.39 KB
needs-review-queue-bot
#29
3508641-nr-bot_g13hb1kn.txt
91 bytes
needs-review-queue-bot
#12
Peek 20-04-2025 18-33.gif
4.03 MB
grimreaper
Issue fork
drupal-3508641
Show commands
Start within a Git clone of the project using the
version control instructions
Add & fetch this issue fork’s repository
Or,
if you do not have
SSH keys set up on git.drupalcode.org
Add & fetch this issue fork’s repository
3508641-define-form-elements
plain diff
MR
!11876
Check out this branch for the first time
Check out existing branch, if you already have it locally
About issue forks
Comments
Comment
#1
24 February 2025 at 10:38
pdureau
created an issue. See
original summary
or
to post comments
Comment
#2
grimreaper
French
France πŸ‡«πŸ‡·
commented
24 February 2025 at 10:39
Assigned:
Unassigned
grimreaper
or
to post comments
Comment
#3
grimreaper
French
France πŸ‡«πŸ‡·
commented
24 February 2025 at 10:41
Issue summary:
View changes
or
to post comments
Comment
#4
grimreaper
French
France πŸ‡«πŸ‡·
commented
24 February 2025 at 10:49
Issue summary:
View changes
or
to post comments
Comment
#5
grimreaper
French
France πŸ‡«πŸ‡·
commented
24 February 2025 at 10:57
Proposed resolution:
In the YAML, a new "form" root level entry to contain those form "props" (ajax, description, etc.).
Or
Normal props declaration, and a mapping system to say "this 'message' prop/slot is sourced by 'description' form property".
or
to post comments
Comment
#6
pdureau
commented
30 March 2025 at 17:01
Let's have a look on all 37 Render Elements from Core:
$ grep -r "\[FormElement(" . | sort | grep -v test
./lib/Drupal/Core/Datetime/Element/Datelist.php:#[FormElement('datelist')]
./lib/Drupal/Core/Datetime/Element/Datetime.php:#[FormElement('datetime')]
./lib/Drupal/Core/Entity/Element/EntityAutocomplete.php:#[FormElement('entity_autocomplete')]
./lib/Drupal/Core/Render/Element/Button.php:#[FormElement('button')]
./lib/Drupal/Core/Render/Element/Checkboxes.php:#[FormElement('checkboxes')]
./lib/Drupal/Core/Render/Element/Checkbox.php:#[FormElement('checkbox')]
./lib/Drupal/Core/Render/Element/Color.php:#[FormElement('color')]
./lib/Drupal/Core/Render/Element/Date.php:#[FormElement('date')]
./lib/Drupal/Core/Render/Element/Email.php:#[FormElement('email')]
./lib/Drupal/Core/Render/Element/File.php:#[FormElement('file')]
./lib/Drupal/Core/Render/Element/Hidden.php:#[FormElement('hidden')]
./lib/Drupal/Core/Render/Element/ImageButton.php:#[FormElement('image_button')]
./lib/Drupal/Core/Render/Element/Item.php:#[FormElement('item')]
./lib/Drupal/Core/Render/Element/LanguageSelect.php:#[FormElement('language_select')]
./lib/Drupal/Core/Render/Element/MachineName.php:#[FormElement('machine_name')]
./lib/Drupal/Core/Render/Element/Number.php:#[FormElement('number')]
./lib/Drupal/Core/Render/Element/PasswordConfirm.php:#[FormElement('password_confirm')]
./lib/Drupal/Core/Render/Element/Password.php:#[FormElement('password')]
./lib/Drupal/Core/Render/Element/PathElement.php:#[FormElement('path')]
./lib/Drupal/Core/Render/Element/Radio.php:#[FormElement('radio')]
./lib/Drupal/Core/Render/Element/Radios.php:#[FormElement('radios')]
./lib/Drupal/Core/Render/Element/Range.php:#[FormElement('range')]
./lib/Drupal/Core/Render/Element/Search.php:#[FormElement('search')]
./lib/Drupal/Core/Render/Element/Select.php:#[FormElement('select')]
./lib/Drupal/Core/Render/Element/Submit.php:#[FormElement('submit')]
./lib/Drupal/Core/Render/Element/Table.php:#[FormElement('table')]
./lib/Drupal/Core/Render/Element/Tableselect.php:#[FormElement('tableselect')]
./lib/Drupal/Core/Render/Element/Tel.php:#[FormElement('tel')]
./lib/Drupal/Core/Render/Element/Textarea.php:#[FormElement('textarea')]
./lib/Drupal/Core/Render/Element/Textfield.php:#[FormElement('textfield')]
./lib/Drupal/Core/Render/Element/Token.php:#[FormElement('token')]
./lib/Drupal/Core/Render/Element/Url.php:#[FormElement('url')]
./lib/Drupal/Core/Render/Element/Value.php:#[FormElement('value')]
./lib/Drupal/Core/Render/Element/VerticalTabs.php:#[FormElement('vertical_tabs')]
./lib/Drupal/Core/Render/Element/Weight.php:#[FormElement('weight')]
./modules/file/src/Element/ManagedFile.php:#[FormElement('managed_file')]
./modules/language/src/Element/LanguageConfiguration.php:#[FormElement('language_configuration')]
The "inside" of form elements
23 of them are direct calls to theme hooks
$ grep -r -A 1000 "\[FormElement(" . | grep '#theme..=' | sort | grep -v test
./lib/Drupal/Core/Datetime/Element/Datelist.php- '#theme' => 'datetime_form',
./lib/Drupal/Core/Datetime/Element/Datetime.php- '#theme' => 'datetime_form',
./lib/Drupal/Core/Render/Element/Checkbox.php- '#theme' => 'input__checkbox',
./lib/Drupal/Core/Render/Element/Color.php- '#theme' => 'input__color',
./lib/Drupal/Core/Render/Element/Date.php- '#theme' => 'input__date',
./lib/Drupal/Core/Render/Element/Email.php- '#theme' => 'input__email',
./lib/Drupal/Core/Render/Element/File.php- '#theme' => 'input__file',
./lib/Drupal/Core/Render/Element/Hidden.php- '#theme' => 'input__hidden',
./lib/Drupal/Core/Render/Element/MachineName.php- '#theme' => 'input__textfield',
./lib/Drupal/Core/Render/Element/Number.php- '#theme' => 'input__number',
./lib/Drupal/Core/Render/Element/Password.php- '#theme' => 'input__password',
./lib/Drupal/Core/Render/Element/Radio.php- '#theme' => 'input__radio',
./lib/Drupal/Core/Render/Element/Range.php- '#theme' => 'input__range',
./lib/Drupal/Core/Render/Element/Search.php- '#theme' => 'input__search',
./lib/Drupal/Core/Render/Element/Select.php- '#theme' => 'select',
./lib/Drupal/Core/Render/Element/Table.php- '#theme' => 'table',
./lib/Drupal/Core/Render/Element/Tableselect.php- '#theme' => 'table__tableselect',
./lib/Drupal/Core/Render/Element/Tel.php- '#theme' => 'input__tel',
./lib/Drupal/Core/Render/Element/Textarea.php- '#theme' => 'textarea',
./lib/Drupal/Core/Render/Element/Textfield.php- '#theme' => 'input__textfield',
./lib/Drupal/Core/Render/Element/Token.php- '#theme' => 'input__hidden',
./lib/Drupal/Core/Render/Element/Url.php- '#theme' => 'input__url',
./modules/file/src/Element/ManagedFile.php- '#theme' => 'file_link',
./modules/file/src/Element/ManagedFile.php- '#theme' => 'file_managed_file',
file_managed_file:

{{ element }}