Filament v4.12 and v5.7: 92% Faster Forms, and the Security Fixes You Still Have to Turn On
Originally published at hafiz.dev Filament shipped its biggest performance release since the v4 launch on August 6. Versions v4.12.6 and v5.7.6 promote the beta the team asked the community to test in late June onto the stable channels for both major versions, and the headline numbers are real: form fields render up to 92% faster, tables up to 52% faster, all in a non-breaking minor release. So run composer update . Today. The same release resolves what the team describes as a handful of CVEs, and staying behind on a release with security content is not a position you want to defend. But here's the thing the announcement undersells, and the reason this post exists: two of the three new security features in this release do nothing until you write code. Updating gets you the patched vulnerabilities and all of the performance. It does not turn on the CSV formula-injection protection, and it does not sanitize a single CSS color value. If you read "please update to keep your application secure" and stop there, you'll be running the new version with the new protections sitting unused. Let's take the release apart properly: what's automatic, what's opt-in, and what the benchmark numbers actually mean for a real admin panel. The performance numbers, and what they measure The team published internal benchmarks with the release, and they're worth reading precisely because of what they measure: median render time per component, not page load. For forms and schema components: | Component | Previous | Now | Change | |---|---|---|---| TextInput | 0.559 ms | 0.044 ms | ~92% faster | Select | 0.634 ms | 0.052 ms | ~92% faster | FileUpload | 0.586 ms | 0.083 ms | ~86% faster | Repeater (10 items, 2 fields) | 28.077 ms | 2.885 ms | ~90% faster | Checkbox | 0.314 ms | 0.033 ms | ~90% faster | Half a millisecond per text input doesn't sound like much until you multiply it by a real resource form, the kind you end up with on any serious admin dashboard. Thirty fields at ~0.5 ms each was 15 ms of pure framework rendering overhead before your queries, your Livewire round trip, or your validation ran at all. That's now about 1.5 ms. And every Livewire interaction that re-renders the form pays this cost again, so the win compounds across a session, not just on first load. The Repeater row is the one that matters most in practice. Repeaters are where Filament forms get heavy: line items on an invoice, variants on a product, questions on a quiz. Going from 28 ms to under 3 ms for a ten-item repeater changes the feel of exactly the forms people complain about. Tables got a second pass too (the team already did a big tables performance round in an earlier version): | Scenario | Previous | Now | Change | |---|---|---|---| 50 rows, 5 TextColumn s, 3 row Action s | 27.53 ms | 13.11 ms | ~52% faster | 50 rows, 5 columns, 1 ActionGroup with 3 Action s | 33.09 ms | 16.73 ms | ~49% faster | One honest caveat the team includes and most coverage will drop: these are internal development benchmarks, and your results will vary. Component-level medians are a fair way to measure framework overhead, but your panel's bottleneck might still be an N+1 query the renderer can't save you from. How they did it, and why it's a little funny The forms speedup comes from ripping out Blade component rendering paths and replacing them with direct PHP rendering. The pull request is refreshingly blunt about the technique: large portions of repeated Blade component calls were replaced with, in the team's own words, our dear friend the old <?php tag. There's a real lesson in that. Blade components are a beautiful authoring experience, and every single one carries framework overhead: resolving the component class, building the data array, rendering the view. That cost is invisible until you're rendering hundreds of them per request, which is exactly what a Filament form does. The fastest Blade component is the one you don't render, and the Filament team just applied that at framework scale. If you've ever profiled a slow Filament page and wondered why so much time sat inside view rendering rather than your own code, this release is the answer arriving. The tables work is subtler: lighter rendering paths for common actions, more efficient attribute handling, and per-record caching of visibility and authorization decisions. That last one deserves a highlight. If your table calls a policy or a visibility closure per action, per record, those decisions were being recomputed repeatedly within a single render. Now they're cached per record. Tables with lots of conditional visibility and authorization logic, which describes most serious multi-user panels, benefit the most. If your panel gates actions by role or tenant the way I set up in the SaaS panel guide, this is the part of the release working for you. The security half, sorted by what actually happens when you update This is where the release notes needed a second read. Sorted by what you get automatically versus what you have to do: Automatic when you update: the patched CVEs. The release resolves several vulnerabilities. The team hasn't itemized them in the announcement beyond "a handful of CVEs", which is normal for coordinated disclosure, and it's also the entire argument for updating now rather than during next month's maintenance window. Also already in if you're current: the MFA ordering fix. A week before this release, v4.12.5 and v5.7.5 quietly shipped a fix titled "Check panel access before presenting MFA challenge." Before that patch, the MFA challenge screen could be presented before panel authorization ran. If you use Filament's multi-factor authentication, that ordering fix alone justified the update, and it rode in with no announcement at all. Opt-in: CSV formula-injection protection. When a spreadsheet app opens a CSV, cell values starting with = , - , + , or a tab character can execute as formulas. An attacker who controls a value that ends up in your admin export (a display name, a note field) can plant a formula that fires on the machine of whoever opens the file. Filament now ships protection that prefixes dangerous cells with a " so they stay text. It's off by default, and for a defensible reason: legitimate values like international phone numbers start with + , and silently rewriting them would corrupt real exports. The team made the right call. But that means the protection only exists in panels where someone consciously enabled it, and "someone" is you, this week, for every exporter that includes user-controlled text. Manual: CSS color sanitization. There's a new Str::sanitizeCssColor() helper for the case where user input ends up inside a style="" attribute. A malicious "color" value can break out of the attribute and inject extra CSS or attributes. The helper closes that, but it's a helper. It sanitizes nothing until you call it at your injection points. Grep your custom columns, entries, and blade views for style= interpolations before deciding you don't have any. Configure it: query builder limits. New maxRules() and maxNestingDepth() methods bound how many rules and how much nesting the Query Builder accepts, enforced in the UI and against tampered payloads. The team files this under performance, but read the failure mode: a hand-crafted payload with an enormous rule tree could previously burn CPU and memory until response times cratered or the server fell over. That's a denial-of-service vector through a form, and if you expose the Query Builder to any non-admin user tier, setting these limits is a security decision, not a tuning knob. So the accurate summary of this release is: the performance is free, the patched CVEs are free, and the three new defenses are tools you now own but haven't picked up. Update first, then budget fifteen minutes for the checklist below. The fifteen-minute audit after updating - Grep for CSV exporters. Anywhere you export tables containing user-controlled text, enable the formula-injection protection, then check whether any legitimate values (phone numbers, negative amounts) get mangled and handle those columns explicitly. - Grep for style= interpolations. Custom columns, infolist entries, and blade views that build inline styles from stored values get wrapped inStr::sanitizeCssColor() . - Bound your query builders. If non-admin users can touch a Query Builder, set maxRules() andmaxNestingDepth() to the most restrictive values your real use cases allow. - Confirm you're on at least .5 of your branch. That's the MFA ordering fix. The current .6 includes it. - Re-run your slowest panel page and enjoy. No action needed, this one's the reward. The quality-of-life additions worth knowing The release also carries a set of smaller changes, a few of which solve real irritations. User and tenant menu items can now be registered in groups instead of one flat list, and the menu actions get memoized once per request as a bonus. Nested modals get modalDismissesParentActions() , so closing an inner modal can abandon a whole multi-step flow instead of forcing users to close each layer individually. Chart widgets can define proper empty states, which finally distinguishes "no data yet" from "broken". And a scattering of dynamic configuration wins: panel content width, sidebar width, dark mode availability, and the theme switcher can all take closures at runtime now, and navigation children can reference parents by a stable key instead of the displayed label, which stops translations from silently breaking your nav hierarchy. None of these changes the argument for updating, but the grouped menus and the stable navigation keys are the kind of small fixes that let you delete workarounds from real codebases. Should you update today? Yes, and this is an easier call than most minor releases. The case is unusually clean: it's a non-breaking minor on both branches, the beta ran in community applications for over a month before promotion, there's security content, and the performance wo
Comments
No comments yet. Start the discussion.