From 8aac72b1452a8e40f620acf0ddbdca8326d92a72 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 28 Jan 2026 12:34:48 +0100 Subject: [PATCH 1/3] [Forms] Revamp the docs about form theming and customization --- form/form_customization.rst | 465 ++++++++----------- form/form_themes.rst | 889 ++++++++++++++++++------------------ forms.rst | 2 + translation.rst | 2 + 4 files changed, 634 insertions(+), 724 deletions(-) diff --git a/form/form_customization.rst b/form/form_customization.rst index 549c5c30866..5a9605c40db 100644 --- a/form/form_customization.rst +++ b/form/form_customization.rst @@ -1,216 +1,164 @@ How to Customize Form Rendering =============================== -Symfony gives you several ways to customize how a form is rendered. In this -article you'll learn how to make single customizations to one or more fields of -your forms. If you need to customize all your forms in the same way, create -instead a :doc:`form theme ` or use any of the built-in -themes, such as the :doc:`Bootstrap theme for Symfony forms `. +This article explains how to customize individual form fields directly in your +templates. For reusable customizations across your entire application, see +:doc:`/form/form_theming`. .. _form-rendering-basics: -Form Rendering Functions ------------------------- +Rendering Forms +--------------- -A single call to the :ref:`form() Twig function ` is -enough to render an entire form, including all its fields and error messages: +The simplest way to render a form is with a single function call: .. code-block:: twig - {# form is a variable passed from the controller via - $this->render('...', ['form' => $form]) - or $this->render('...', ['form' => $form->createView()]) #} {{ form(form) }} -The next step is to use the :ref:`form_start() `, -:ref:`form_end() `, -:ref:`form_errors() ` and -:ref:`form_row() ` Twig functions to render the -different form parts so you can customize them adding HTML elements and attributes: +This renders everything: the ``
`` tag, all fields, labels, errors, and +the closing tag. For more control, render each part separately: .. code-block:: html+twig {{ form_start(form) }} -
- {{ form_errors(form) }} -
+ {{ form_errors(form) }}
-
- {{ form_row(form.task) }} -
-
- {{ form_row(form.dueDate) }} -
+
{{ form_row(form.firstName) }}
+
{{ form_row(form.lastName) }}
+ + {{ form_row(form.email) }} + {{ form_rest(form) }} {{ form_end(form) }} -The ``form_row()`` function outputs the entire field contents, including the -label, help message, HTML elements and error messages. All this can be further -customized using other Twig functions, as illustrated in the following diagram: +Rendering Individual Field Parts +-------------------------------- + +The ``form_row()`` function renders a complete field (label, widget, help, +errors). For finer control, render each part separately: .. raw:: html -The :ref:`form_label() `, -:ref:`form_widget() ` (the HTML input), -:ref:`form_help() ` and -:ref:`form_errors() ` Twig functions give you total -control over how each form field is rendered, so you can fully customize them: - .. code-block:: html+twig -
+
{{ form_label(form.dueDate) }} {{ form_widget(form.dueDate) }} - {{ form_help(form.dueDate) }} - -
- {{ form_errors(form.dueDate) }} -
+
{{ form_errors(form.dueDate) }}
.. warning:: - If you're rendering each field manually, make sure you don't forget the - ``_token`` field that is automatically added for CSRF protection. + When rendering fields manually, always include ``{{ form_rest(form) }}`` + before ``form_end()``. This renders hidden fields (like CSRF tokens) and + any fields you may have forgotten. + +Form Variables +-------------- + +Each form field exposes variables through its ``vars`` property. You can read +these variables directly or override them when calling rendering functions: + +.. code-block:: twig + + {# reading field variables #} + {{ form.email.vars.id }} + {{ form.email.vars.full_name }} + {{ form.email.vars.value }} + + {# overriding variables when rendering #} + {{ form_label(form.email, 'Your Email Address') }} + {{ form_widget(form.email, {attr: {class: 'email-input', autofocus: true}}) }} + {{ form_row(form.email, { + label: 'Contact Email', + label_attr: {class: 'required'}, + attr: {placeholder: 'user@example.com'} + }) }} + +This is useful for building custom markup while preserving form functionality: + +.. code-block:: html+twig - You can also use ``{{ form_rest(form) }}`` (recommended) to render any - fields that aren't rendered manually. See - :ref:`the form_rest() documentation ` below for - more information. + .. note:: - Later in this article you can find the full reference of these Twig - functions with more usage examples. + Variables passed to ``form_widget(form)`` or ``form_row(form)`` are not + applied recursively to child fields. Render children individually to + customize them. + +Check out the complete :ref:`list of form variables `. .. _reference-forms-twig-field-helpers: Form Field Helpers ------------------ -The ``form_*()`` helpers shown in the previous section render different parts of -the form field, including all its HTML elements. Some developers and designers -struggle with this behavior, because it hides all the HTML elements in form -themes which are not trivial to customize. - -That's why Symfony provides other Twig form helpers that render the value of -each form field part without adding any HTML around it: - -* ``field_name()`` -* ``field_value()`` -* ``field_label()`` -* ``field_help()`` -* ``field_errors()`` -* ``field_choices()`` (an iterator for choice fields; e.g. for `` - + {% for label, value in field_choices(form.country) %} {% endfor %} -Form Rendering Variables ------------------------- - -Some of the Twig functions mentioned in the previous section allow you to pass -variables to configure their behavior. For example, the ``form_label()`` -function lets you define a custom label to override the one defined in the form: - -.. code-block:: twig - - {{ form_label(form.task, 'My Custom Task Label') }} - -Some :doc:`form field types ` have additional rendering -options that can be passed to the widget. These options are documented with each -type, but one common option is ``attr``, which allows you to modify HTML -attributes on the form element. The following would add the ``task_field`` CSS -class to the rendered input text field: - -.. code-block:: twig - - {{ form_widget(form.task, {'attr': {'class': 'task_field'}}) }} - -.. note:: - - If you're rendering an entire form at once (or an entire embedded form), - the ``variables`` argument will only be applied to the form itself and - not its children. In other words, the following will **not** pass a - "foo" class attribute to all of the child fields in the form: +Available helpers: - .. code-block:: twig - - {# does **not** work - the variables are not recursive #} - {{ form_widget(form, { 'attr': {'class': 'foo'} }) }} - -If you need to render form fields "by hand" then you can access individual -values for fields (such as the ``id``, ``name`` and ``label``) using its -``vars`` property. For example to get the ``id``: - -.. code-block:: twig - - {{ form.task.vars.id }} - -.. note:: - - Later in this article you can find the full reference of these Twig - variables and their description. +* ``field_name()`` - The ``name`` attribute value +* ``field_value()`` - The current value +* ``field_label()`` - The label text +* ``field_help()`` - The help text +* ``field_errors()`` - Error messages as an iterable +* ``field_choices()`` - Label/value pairs for choice fields Form Themes ----------- -The Twig functions and variables shown in the previous sections can help you -customize one or more fields of your forms. However, this customization can't -be applied to the rest of the forms of your app. - -If you want to customize all forms in the same way (for example to adapt the -generated HTML code to the CSS framework used in your app) you must create a -:doc:`form theme `. +The Twig functions and variables above help you customize individual fields. +To customize all forms consistently (e.g., to match your CSS framework), use a +built-in :doc:`form theme ` or create your own. .. _reference-form-twig-functions-variables: - -Form Functions and Variables Reference --------------------------------------- - .. _reference-form-twig-functions: -Functions -~~~~~~~~~ +Twig Form Functions +------------------- .. _reference-forms-twig-form: form(form_view, variables) -.......................... +~~~~~~~~~~~~~~~~~~~~~~~~~~ -Renders the HTML of a complete form. +Renders a complete form including the opening and closing tags. .. code-block:: twig - {# render the form and change the submission method #} - {{ form(form, {'method': 'GET'}) }} + {{ form(form, {method: 'GET'}) }} -You will mostly use this helper for prototyping or if you use custom form -themes. If you need more flexibility in rendering the form, you should use -the other helpers to render individual parts of the form instead: +This is convenient for prototyping, but use the other helpers for more control: .. code-block:: twig @@ -220,71 +168,61 @@ the other helpers to render individual parts of the form instead: {{ form_row(form.name) }} {{ form_row(form.dueDate) }} - {{ form_row(form.submit, { 'label': 'Submit me' }) }} + {{ form_row(form.submit, {label: 'Submit me'}) }} {{ form_end(form) }} .. _reference-forms-twig-start: form_start(form_view, variables) -................................ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Renders the start tag of a form. This helper takes care of printing the -configured method and target action of the form. It will also include the -correct ``enctype`` property if the form contains upload fields. +Renders the ``
`` opening tag with the configured method, action, and +``enctype`` (when the form has file uploads). .. code-block:: twig - {# render the start tag and change the submission method #} - {{ form_start(form, {'method': 'GET'}) }} + {{ form_start(form, {method: 'GET'}) }} .. _reference-forms-twig-end: form_end(form_view, variables) -.............................. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Renders the end tag of a form. +Renders the ``
`` closing tag. By default, it also calls ``form_rest()`` +to render any remaining fields, but you can disable that: .. code-block:: twig {{ form_end(form) }} -This helper also outputs ``form_rest()`` (which is explained later in this -article) unless you set ``render_rest`` to false: - -.. code-block:: twig - - {# don't render unrendered fields #} + {# disable automatic form_rest() call #} {{ form_end(form, {render_rest: false}) }} .. _reference-forms-twig-label: form_label(form_view, label, variables) -....................................... +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Renders the label for the given field. You can optionally pass the specific -label you want to display as the second argument. +Renders the ``