Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Form Rendering Variables

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Find the form_row() documentation. There is one super important thing that almost all of these functions share: their last argument is something called variables.

These variables are key to controlling how each part of each field is rendered. And it's explained a bit more at the bottom. Yep - this table describes the most common "variables" - which are kind of like "options" - that you can pass to most fields, including label.

Let's override that variable for the title. Pass a second argument to form_row(): an array with a label key. How about, Article title.

... lines 1 - 5
{{ form_start(articleForm) }}
{{ form_row(articleForm.title, {
label: 'Article title'
}) }}
... lines 10 - 14
{{ form_end(articleForm) }}
... lines 16 - 17

Try that! Reload the form. Boom! Label changed!

Discovering the Form Variables

There are tons of variables that you can pass to the form rendering functions to change how the field is rendered. And the list of those variables will be slightly different for each field type. The best way to see all the possibilities is back inside our best friend: the form profiler.

Click on the field you want to customize - like title. I'll collapse the options stuff. Remember: options are what we can pass to the third argument of the add() function in our form class.

For rendering, we're interested in the "View Variables". Behind the scenes, each part of each field is rendered by a mini Twig template that lives inside Symfony. We'll see this later. These variables are passed to those Twig templates and used to control, well, almost everything.

Hey! There's the label variable we just overrode! Notice, it's null: the values inside the profiler represent the values at the moment the form object is passed into the Twig template. So, if you override a value, it won't show up here. No big deal - just don't let that surprise you.

Ah, and there's a help message and a whole bunch of other things that help the form do its job, like full_name, which will be the name attribute, and even the id attribute.

By the way, if it's useful, in addition to overriding these variables, you can access them directly in your template. I don't need it here, but you could, for example print articleForm.title.vars.id.

If you go back and look at your form, that will print the id attribute that's used for the form field. Pretty cool, though, the real purpose of variables is to override them via the form functions.

form_label(), form_widget(), form_help(), form_errors()

Using form_row() gave us more flexibility, because we can reorder the fields and override the field's variables. If you need a little bit more flexibility, another option is to render the 4 components of each field independently.

For example, get rid of form_row. And, instead, render each part manually: {{ form_label(articleForm.title) }}, and for this function, the second argument is the label. Then {{ form_errors(articleForm.title) }} for the validation errors, {{ form_widget(articleForm.title }} to print the input field, and finally {{ form_help(articleForm.title }}.

... lines 1 - 6
{{ form_label(articleForm.title, 'Article title') }}
{{ form_errors(articleForm.title) }}
{{ form_widget(articleForm.title) }}
{{ form_help(articleForm.title) }}
... lines 11 - 19

Let's see how this compares to using form_row(). Refresh! Hmm - it's almost the same. But if you look more closely, of course! The other fields are wrapped in a form-group div, but the title no longer has that!

When you render things at this level, you start to lose some of the special formatting that form_row() gives you. Sure, it's easy to re-add that div. But form_row also adds a special error class to that div when the field has a validation error.

For that reason, let's go back to using form_row().

... lines 1 - 6
{{ form_row(articleForm.title, {
label: 'Article title'
}) }}
... lines 10 - 17

A little bit later, we're going to learn how we can use form_row(), but completely customize how it looks for one specific form, or across your entire site! We'll do this by creating a "form theme". It's kind of the best of both worlds: you can render things in the lazy way, but still have the control you need.

But before we get there - let's learn how to create an "edit" form so we can update articles!

Leave a comment!

Login or Register to join the conversation
Joao P. Avatar
Joao P. Avatar Joao P. | posted 3 years ago

form_widget is also showing the error msg, so if I use form_errors ans form_widget I get two error msgs.


Hey Joao P.

the form_widget function will render the 4 parts of a form field (label, field, error, and help) and then such field will be marked as rendered. I'm surprise that you're seeing the same error twice. Is it possible that you are rendering your form in a different way?


Joao P. Avatar
Joao P. Avatar Joao P. | posted 3 years ago

Hi, the custom validation error is not triggered if the title starts with 'the borg'.


Hey Joao P.

What assertions do you have on the title property? You may have a min length assertion


Does it make a difference if you define the label in your ArticleFormType.php? What advantages does each side have? Why would I want to define those in Twig, but not the other way around? Does it affect translations?

Default user avatar

I know how to change the label on form_start() since it has single element.
How do i change the label with form_widgets() when it has multiple options(password, repeat_password)?


Hey @wuwu

You can change the label directly in your form class https://symfony.com/doc/cur...
or in the template by sending an object with the options as second parameter


Default user avatar

how can I place a subscribtion form with a text bar and a submit button which can persist the email in database in the base twig so it can be shown in every page.


Hey Mike,

Yeah, you can do it, just create a custom Twig function and return a proper form view from there, then just render it as you always do. You can handle this for with JS, or you can submit it as a normal form to a specific route you created that would handle the form data and where you would redirect user back to the referer URL. Well, I think submitting the form via AJAX request is more cool, but not too imtportant, depends on your needs.


Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
        "knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
        "knplabs/knp-time-bundle": "^1.8", // 1.8.0
        "nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
        "php-http/guzzle6-adapter": "^1.1", // v1.1.1
        "sensio/framework-extra-bundle": "^5.1", // v5.2.1
        "stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
        "symfony/asset": "^4.0", // v4.1.6
        "symfony/console": "^4.0", // v4.1.6
        "symfony/flex": "^1.0", // v1.17.6
        "symfony/form": "^4.0", // v4.1.6
        "symfony/framework-bundle": "^4.0", // v4.1.6
        "symfony/orm-pack": "^1.0", // v1.0.6
        "symfony/security-bundle": "^4.0", // v4.1.6
        "symfony/serializer-pack": "^1.0", // v1.0.1
        "symfony/twig-bundle": "^4.0", // v4.1.6
        "symfony/validator": "^4.0", // v4.1.6
        "symfony/web-server-bundle": "^4.0", // v4.1.6
        "symfony/yaml": "^4.0", // v4.1.6
        "twig/extensions": "^1.5" // v1.5.2
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0", // 3.0.2
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.7
        "fzaninotto/faker": "^1.7", // v1.8.0
        "symfony/debug-bundle": "^3.3|^4.0", // v4.1.6
        "symfony/dotenv": "^4.0", // v4.1.6
        "symfony/maker-bundle": "^1.0", // v1.8.0
        "symfony/monolog-bundle": "^3.0", // v3.3.0
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.1.6
        "symfony/profiler-pack": "^1.0", // v1.0.3
        "symfony/var-dumper": "^3.3|^4.0" // v4.1.6