trsteel/html-form-validation-bundle
A Symfony bundle for adding HTML5-based client-side validation to forms, helping catch required fields and input constraints in the browser before submission. Useful for improving form UX with minimal setup.
composer require trsteel/html-form-validation-bundle. Because this is a low-adoption bundle it is almost certainly not in Symfony Flex recipes, so register it manually in config/bundles.php:
Trsteel\HtmlFormValidationBundle\TrsteelHtmlFormValidationBundle::class => ['all' => true],
bin/console config:dump-reference trsteel_html_form_validation
bin/console debug:container --tag=form.type_extension
The output tells you the real config keys and whether it ships a form type extension, a Twig extension, or both.DependencyInjection/Configuration.php and any Form/Extension/*TypeExtension.php — that is where the behaviour lives. The package is small enough to read end-to-end in minutes, which you should do given the 2-star / 0-dependent profile.{{ form_start(form) }}, submit an invalid payload, and verify the browser no longer blocks submission — i.e., the rendered <form> now carries a novalidate attribute (or the HTML5 required/pattern attributes are gone, depending on how the bundle is implemented).config/packages/trsteel_html_form_validation.yaml and let every form render without native browser validation. This is the simplest pattern and the one the README targets.prod/dev but leave it off where you actually want browsers to short-circuit obviously-bad input (e.g. a login form). Symfony's when@env blocks work well here.FormType, individual forms can usually still control the root attribute directly:
$form = $this->createForm(ContactType::class, $data, [
'attr' => ['novalidate' => 'novalidate'],
]);
Conversely, if you only need it on one form, skip the global config entirely and set the attribute by hand — that is often the whole "bundle" in one line of code.form_start() / form_end(); the attribute is injected on the root <form> tag. Custom form themes that hardcode <form> will bypass it.$form->isSubmitted() && $form->isValid() reliably produces field errors rendered by form_errors() / form_row(). This is the main day-to-day reason to use it.WebTestCase:
$client->request('GET', '/contact');
$this->assertSelectorExists('form[novalidate]');
Add this to your smoke tests so a Symfony upgrade that silently breaks the extension is caught.AbstractTypeExtension on Symfony\Component\Form\Extension\Core\Type\FormType that appends novalidate to $options['attr'], registered with the form.type_extension tag.composer.json, read the source before trusting it, and plan for the form-type-extension replacement above as your exit strategy.composer.json require block for the supported symfony/form range. A recent "last release" date does not guarantee it tracks current Symfony majors — run the container compile (bin/console cache:clear) and a form-rendering test immediately after install.novalidate ≠ disabling constraints. The attribute only turns off browser validation. HTML5 attributes such as required, type="email", min/max are still emitted (unless the bundle strips them), so CSS :invalid / :valid selectors and any JS reading element.validity will still fire.attr on the form in PHP and the bundle injects novalidate, know which wins. Dump form.vars.attr in Twig ({{ dump(form.vars.attr) }} with the debug bundle) when the attribute unexpectedly appears or disappears.FormType affect the root form and every child form type unless guarded with if ($form->isRoot()). Check the bundle's extension for that guard; otherwise nested collection forms may also receive the attribute (usually harmless, occasionally confusing in markup).<form> tags bypass everything. Any template that writes <form> by hand instead of using form_start() gets no attribute.bin/console config:dump-reference is your ground truth — do not guess key names from the README, which is likely stale.bundles.php; (2) config file loaded and named after the correct alias; (3) cache cleared; (4) template uses form_start(); (5) inspect the raw HTML response, not just the DOM after JS ran.AbstractTypeExtension::getExtendedTypes() returning [FormType::class] plus a configureOptions/buildView override is the canonical Symfony way to do this — worth knowing regardless of whether you keep the bundle, since it makes the dependency disposable.How can I help you explore Laravel packages today?