shopsys/jsformvalidator-bundle
A Symfony bundle from Shopsys that adds client-side JavaScript form validation by translating server-side constraints into instant browser feedback. It helps build responsive forms with consistent rules, customizable messages, and fewer validation round trips.
shopsys/jsformvalidator-bundle is a Symfony bundle that projects your server-side validator metadata (attributes/annotations/XML/YAML on entities and form types) into the rendered form as client-side validation rules, so the browser can fail fast before a POST. For a Laravel developer, think of it as taking your FormRequest rules and emitting them as data-* hooks plus a JS validator, so you don't hand-write a parallel rule set in JavaScript. It is a fork of the original JsFormValidatorBundle maintained by Shopsys — the packaging shows 0 stars/0 dependents, but that mostly reflects it being consumed inside the Shopsys monorepo rather than a healthy standalone ecosystem.
As of v2.0.0 the fork supports Symfony 6 and 7, which removes the biggest practical objection to using it on a current project. The changelog entry is one line ("Added Symfony 6 and 7 support"), so treat the major version bump as a signal that the supported floor moved up as well — see Gotchas before upgrading an existing install.
Minimal steps (Flex-style paths; adjust for older Symfony app/config layouts):
Install and let Composer resolve Symfony constraints. On Symfony 6/7 you should be able to take the v2 line directly:
composer require shopsys/jsformvalidator-bundle:^2.0
If it conflicts, check the fork's composer.json for its PHP floor and supported symfony/* ranges before pinning; on Symfony 4.4/5.x you will need the 1.7 line instead.
Register the bundle in config/bundles.php. The exact class name varies by fork generation — confirm by looking under vendor/shopsys/jsformvalidator-bundle/src/:
return [
// ...
Shopsys\JsFormValidatorBundle\ShopsysJsFormValidatorBundle::class => ['all' => true],
];
Add the bundle config for the root key (check DependencyInjection/Configuration.php for the authoritative keys) — typically enabling attribute/annotation mapping and choosing static vs. dynamic (AJAX) constraint delivery:
# config/packages/js_form_validator.yaml
js_form_validator:
enable_annotations: true
# dynamic/delayed mode options if you don't want metadata inline in HTML
On a v2/Symfony 6-7 stack, prefer PHP attributes over annotations; confirm in Configuration.php whether the annotation toggle is still honoured or now means attributes only.
Make the JS available. Either install the bundle's public assets:
php bin/console assets:install public --symlink
or import the source file from vendor/.../Resources/public/js into your Encore/Webpack entry to avoid a second copy of jQuery.
Wire it into your base layout, after jQuery and after the form markup:
<script src="{{ asset('bundles/jsformvalidator/js/jsFormValidator.js') }}"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
// exact initializer per README; commonly a plugin call on the form
$('form').jsFormValidator();
});
</script>
Add the bundle's form theme block if you use a custom theme, or ensure your theme preserves the data-* attributes the bundle adds; register it via twig.form_themes if needed.
First use case: a registration/contact form with NotBlank, Email, and Length on a DTO or entity. Render it, submit empty, and confirm errors appear without a network round-trip. Then view source: fields should carry validation metadata, and in dynamic mode you should see a constraint-fetch request in the network tab. If neither is true, the theme or the init call is the problem — not the constraints.
Where to look first: the bundle's Resources/public/js entry point, Resources/views/Form theme, and DependencyInjection/Configuration.php. Forks often keep the upstream README, so also diff against the original repository when something is undocumented — and on v2 diff against the v1.7 tag when a config key or asset path doesn't behave as the old docs describe.
constraints option) and let the bundle translate them. Don't re-implement rules in JS; the moment you do, the fork's maintenance risk becomes your maintenance burden.validation_groups on the form type and make sure the client side is told the same groups. Mismatched groups are the most common cause of "client passes, server rejects" or the reverse. Test each form's actual submit path, not just the default group.App\Validator\StrongPassword does not unless you register a matching JS validator through the bundle's extension point (config mapping or JS registration). Until then it is silently skipped client-side while still enforced server-side — which is fine, but surprising if you expected parity.turbo:load (or turbo:render) rather than only DOMContentLoaded, or validation will disappear after the first navigation.$form->isValid() on every submit and return proper 422/JSON errors for XHR submissions. This is the same discipline as never trusting Laravel's front-end required attributes.form_row/form_widget. A broad override will silently strip the metadata attributes and disable validation without an error.composer.json and the Resources/views diff before bumping, and keep 1.7 pinned if you are still on Symfony 5.v1.7.0...v2.0.0) for renamed config keys, changed DI service ids, moved asset paths, and changes to the data-* attribute names or the initializer signature. Re-verify each form's rendered HTML after the bump: a silent theme or attribute rename disables validation with no error, and your existing JS init call may now be a no-op.assets:install is required for the bundles/... asset path. If you bundle through Encore, import from vendor/.../Resources/public/js and do not let the bundle ship a second jQuery — duplicate jQuery silently breaks plugins rather than throwing. Re-check the built asset path after the major bump; it may have moved under a new directory name.data-* attributes. If they're missing, the problem is Twig, not validation. This is the most likely casualty of a theme that predates v2.validators.en.yaml etc.); client-side messages are often hardcoded in the bundle JS or need a separate catalog. Budget time for i18n if you're not English-only.UniqueEntity, File, Callback, and anything DB- or request-dependent will be skipped or always pass client-side. Keep them server-side and don't advertise them as instant feedback._token, IDs) are typically ignored. Don't expect CSRF or identifier fields to have client rules; that's normal.test for deterministic assertions, or keep it enabled in dev to catch projection regressions early — pick one deliberately.Configuration.php?vendor/.How can I help you explore Laravel packages today?