Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Jsformvalidator Bundle Laravel Package

fp/jsformvalidator-bundle

Symfony bundle that adds client-side JavaScript validation to forms. It generates rules from Symfony form constraints, supports AJAX checks, and helps keep front-end and back-end validation consistent while reducing unnecessary server requests.

View on GitHub
Deep Wiki
Context7

Getting Started

The bundle exists to give you a single source of truth for validation: the constraints you already declare on entities/DTOs (or in the constraints option of a form field) are serialized into the page, and the bundle's JavaScript enforces them in the browser. You stop hand-writing required, minlength, and regex checks that drift away from the server.

Minimal steps:

  1. composer require fp/jsformvalidator-bundle:"^1.8" — 1.8.0 is the first stable release of the Symfony 8 baseline branch: PHP 8.4+, symfony/* ^8.0, Twig 3, PHPUnit 13. It will not install on Symfony 5.4/6.4/7.x; those apps must stay on the 1.7 line (which is still beta-flavoured). Check composer.json before assuming it drops into your project.
  2. Register the bundle: Fp\JsFormValidatorBundle\FpJsFormValidatorBundle::class => ['all' => true] in config/bundles.php.
  3. Wire the client assets. The bundle ships JS under its Resources/public (classic bin/console assets:install --symlink path) and a modern entry point; pick whichever matches your AssetMapper/Encore setup. The single most common "it doesn't work" cause is a 404 on that script — confirm the global (a jsfv-style object) exists in the browser console before debugging anything else.
  4. Optionally add config/packages/fp_js_form_validator.yaml — the config class in DependencyInjection/Configuration.php is the authoritative list of keys for your installed version, so read it rather than copying blog snippets.

First use case: a registration form. Add NotBlank, Email, Length constraints to the model, render the form exactly as you do today, and submit garbage. You should get inline errors with no network round-trip. Then disable JS and confirm the server rejects the same input — if it doesn't, stop and fix that before going further. A good second case is a payment/checkout field: Luhn is now mirrored client-side (new in 1.8.0), so credit card numbers, SIRET and similar checksummed values validate without a round-trip.

Where to look first: README.md for the minimal wiring, the test fixtures (they enumerate exactly which constraints are supported and produce the expected rule shapes), and src/Resources/public/js for the extension API.

Implementation Patterns

  • Constraint-first, always. Put rules on the model or in the field's constraints array; both paths feed the same metadata. Forms that build rules ad hoc in JS are the ones that rot.
  • Lean on the checksum constraint. Luhn (added in 1.8.0) mirrors Symfony's LuhnValidator and is a drop-in for card numbers, SIRET/SIREN and similar. Combine it with a Regex for the digit-group format, since Luhn alone accepts any checksum-valid digit string.
  • Selective disabling. There's a global switch plus per-form/per-field options exposed through the bundle's form type extension (see Form/Extension). Use it for forms with heavy custom JS, third-party widgets, or upload fields where client-side rules just get in the way.
  • Collections need re-binding. Prototype rows added by your own "Add row" JS are injected after the bundle initialised, so they submit unvalidated unless you call the bundle's init on the new node. This is the #1 day-to-day integration chore. 1.8.0 improved the condition for adding parent forms to the validation queue — embedded parent/child forms are queued more reliably now — but dynamically injected rows still need explicit init, and it's worth re-testing your embedded-form cases against this release rather than assuming the old workaround is still required.
  • Custom constraints are a two-file contract. Server side stays a normal Symfony Constraint + ConstraintValidator; client side you register a matching validator via the bundle's addConstraint/addValidator API. Write a test for each side — they drift silently.
  • Async rules are yours to build. Unique-email/username checks aren't expressible in the generated payload. Either accept a round-trip or write a custom JS validator that calls a controller, debounced.
  • Keep messages translatable. Client messages come from the constraint's message and the request locale, so they match server output — provided the payload is generated per request (see caching gotcha below).
  • Testing pattern that pays off: in WebTestCase, assert the rendered payload contains a rule per constraint (catches silent regressions when a constraint is renamed), and keep one JS-less functional test asserting server rejection. The repo's own Cypress suite is a good template for the browser side; it now runs on Cypress 15.20.x with coverage gates.
  • Treat the client as UX, not validation. Never let a controller assume the JS ran; API endpoints and non-JS clients hit the same routes.

Gotchas and Tips

  • Symfony 8 only. This is a baseline jump, not a compat expansion: PHP 8.4+, Symfony 8.0, Twig 3. If you maintain apps on 5.4/6.4/7.x you are on the 1.7 line and do not get Luhn, the parent-form queue fix, or the CI/static-analysis hardening. Plan multi-app estates accordingly.
  • Partial coverage is the headline risk. Expression, Callback, UniqueEntity, Entity, File/Image, and anything with a service dependency can't be translated to JS. Expect a meaningful slice of your constraints to fall back to server-side only — budget for that instead of being surprised by it. (The supported list grows slowly; Luhn is the 1.8.0 addition.)
  • PCRE ≠ JavaScript regex. Patterns using \p{L}, possessive quantifiers, or PCRE-only constructs will either fail to compile or behave differently in RegExp. Keep Regex constraints in the browser to simple character classes, and test the boundary cases.
  • Full-page caching breaks it. The rule payload is rendered per request and embeds the locale and form structure. Cache a page with a form and you'll ship stale rules and the wrong language. Exclude form pages from HTTP cache, or render the payload via ESI.
  • CSP. If the bundle injects an inline <script>, a strict Content-Security-Policy will block it silently apart from a console violation. Either allow a nonce or render the assets/init yourself.
  • Turbo / partial navigation. Full-page-load injection doesn't re-run on Turbo Drive body swaps or after AJAX-loaded forms; re-init explicitly, or you get forms that look validated but aren't.
  • Double-messaging with HTML5. Symfony already emits required/maxlength/pattern for some constraints. Add novalidate to the form so the browser's bubble doesn't compete with the bundle's error rendering.
  • Debugging. Inspect the serialized rule payload in the page source and the runtime object in the console; a missing rule there means a metadata/constraint-mapping problem, not a JS bug. Clear the validator metadata cache when constraints change and behaviour doesn't. If an embedded form isn't validating, check the parent-form queue behaviour changed in 1.8.0 before writing a workaround that may now be redundant.
  • Extension points worth knowing: custom JS validators, the form type extension (disable/extend per field), template/form-theme overrides for error rendering, and an event/DIC hook for altering generated settings if you need per-tenant behaviour.
  • Maintenance risk, slightly improved but still real. 1.8.0 brings PHPStan with a clean baseline, enforced PHP/JS coverage gates, Dependabot across Composer/npm/Actions, and green CI on PHP 8.4/8.5/8.6 plus browser tests — a genuine quality signal. But the project is still low-star with essentially no dependents, and the entire supported matrix is Symfony 8. Pin ^1.8, keep custom validators small and isolated so they survive an upgrade or a swap to hand-written validation, and be prepared to own fixes.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect