A few weeks ago, the Filament team asked for your help testing a beta release for v4 and v5 that was intended to have a massive improvement in performance for complex Filament applications. Well, you all delivered, and thanks to your help, as of versions v4.12.6 and v5.7.6, we have released these improvements onto our stable release channels for everyone to use!
In addition to these performance improvements, we have also released some security updates to resolve a handful of CVEs, so if you are running Filament v4 or v5, please update to these most recent releases to keep your application secure!
Now, without further ado, let’s talk performance!
Major performance upgrades
Let's jump right in with the stars of the show: the massive performance improvements we see as a part of the updates to both our forms and tables packages.
Forms & schema
First and foremost, behind the scenes for this update, the team has done a lot of work optimizing how forms are rendered within Filament to improve the overall render time for form fields. And, well, we were shocked by the results. We'll just let them speak for themselves here:
| Component | Previous Release | Current Release | 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 |
While all of these are incredible results, the standout winner here is the 90% faster render speed of a Repeater with 10 items, each containing two fields. Because repeaters are often used in Fliament to represent large lists of data, having such a drastic reduction in render time will help large, data-heavy forms become immensely more performant, even as the number of repeater items continues to grow over time.
Now, you might be asking specifically how we managed to cut down on median render time by such a significant amount. For more details, make sure to give the PR a look and see all of the changes that were made; however, in short, we replaced a large portion of our repeated Blade component rendering paths with direct component rendering using our dear friend, the old <?php tag. This change substantially reduces the framework overhead for every single field and schema component you include in your Filament form.
Please note, the performance improvement numbers shown above are from our own internal benchmarks during development. Depending on your specific application, your results may vary.
Tables
If you've been around the Filament community for a bit, you may remember that, not too many versions ago, the Filament team put together a big performance improvement version for the tables package, which already dramatically boosted the performance of tables, specifically when they includes lots and lots of data.
However, for the latest releases of Filament v4 and v5, alongside the updates to forms and schema components, the team has spent even more time optimizing Filament tables to make them even snappier and performant. Again, I'll let the numbers speak for themselves, here:
| Scenario | Previous Release | Current Release | Change |
|---|---|---|---|
50 rows, 5 TextColumns, 3 row Actions |
27.53 ms | 13.11 ms | ~52% faster |
50 rows, 5 TextColumns, 1 ActionGroup with 3 inner Actions per row |
33.09 ms | 16.73 ms | ~49% faster |
Once again, the team was thrilled that we were able to find even more areas to improve table performance, because large, data-heavy tables are a core backbone of why many developers opt to use Filament in the first place!
Unlike the forms and schema update, where much of the gain in performance was due to refactoring existing code away from Blade components, the changes here are much more subtle. You can find the full list of changes in this PR, but in short, the main improvements came from adding lighter rendering paths for common actions and components, handling attributes more efficiently, and adding in per-record caching of repeated visibility and authorization decisions.
All of these changes work together to, in many cases, significantly reduce the amount of server-side work that needs to take place when rendering and interacting with tables. Specifically, applications that have a lot of rows, columns, actions, conditional visibility rules, and/or authorization checks should benefit the most from these changes.
Just like with the forms/schema benchmarks, these improvement numbers are also just benchmarks that were obtained by the team during the development process. Depending on your application, your results may vary.
Other updates
Along with these two huge performance updates, the team has also shipped a handful of smaller performance, security, and quality-of-life updates that we think you'll really enjoy!
Performance
Bound query-builder rule count and nesting depth
With the new maxRules() and maxNestingDepth() methods that have been added to the Query Builder, you can now reliably limit the number of rules and conditions that can be applied to a given query. These methods limit both the front-end UI from giving users the option to exceed these limits as well as rejecting received payloads that are over the limit.
Previously, large rule trees (whether intended or tampered with) could cause excessive CPU and memory usage during processing leading to slow response times, runaway infrastructure costs, or outright server outages.
Security features
CSS color value sanitization
When injecting user input into HTML through a style="" attribute, Filament now provides a Str::sanitizeCssColor() helper that allows you to sanitize it to prevent the injection of extra attributes.
Opt-in CSV formula-injection protection
When opening a CSV in a spreadsheet software like Excel, cell values beginning with characters such as =, -, +, <Tab>, or other similar characters can be interpreted as formulas. Because of this, Filament has now added an opt-in protection system that will deliberately prefix all cells beginning with potentially-malicious characters with a " so they remain text when opening the CSV in the aforementioned spreadsheet software.
We have intentionally made this feature opt-in because legitimate values like international phone numbers (+) would be altered.
Quality of life
Group user and tenant menu items
Previously, user and tenant menus rendered as one flat list, which made the lists of options difficult to scan for large applications that render lots of menu items.
Now, developers can register multiple groups of menu items, each of which will get rendered as visually separate lists to improve readability. Also, as an added bonus, Filament will now memoize these menu actions once per request, which avoids constructing actions multiple times, giving a little boost to performance.
Allow modal dismissal to cancel parent actions
For all the modal-nesters out there, this one is for you!
Prior to this update, closing a modal inside a chain of nested actions only dismissed the innermost modal. Sometimes, this is the desired outcome, but often times developers want to close the innermost modal and all of its parent actions.
This update introduces the modalDismissesParentActions() method, which allows all methods of closing a modal (close button, escape key, etc) to cancel all parents or unwind to a given, named parent action. This works wonders for multi-step workflows that sometimes need an "abandon this flow" interaction, rather than the previous solution of users needing to close each modal individually.
Create empty states for chart widgets
As of this release, you can now create custom empty states for your chart widgets, lending to a more polished and professional looking dashboard that distinguished a broken chart state from a valid, but empty state.
Small, but useful additions
Along with the larger changes listed above, the Filament team has added a few smaller fixes and features to this release as well:
- Allow click-through modals & slide-overs - you can now opt in to remove the click-blocking backdrop from a modal or slide-over, allowing users to interact with content behind the modal or slide-over while it is showing.
- Reference navigation parents by stable key - whereas child navigation items previously depended primarily on the parent's displayed label text, they now can be referenced using a stable navigation key, preventing unexpected breakages with translations, renaming, or dynamically-generated labels.
- Control search-term splitting per-column - you can now configure search term splitting on a per-column basis rather than for the entire table.
- Conditionally hide export columns - showing and hiding columns in an export can now be configured dynamically via closures at runtime.
- Configure the XLSX writer immediately after opening - you can now configure the XLSX writer prior to writing anything to the file, enabling support for custom rows like title rows, explanatory rows, or preliminary formatting without having to replace the entire rendering pipeline.
- Dynamically configure panel content width - you can now dynamically configure the content width of content in a panel by passing a closure to the
maxContentWidth()method. - Dynamically configure dark mode & theme switcher - you can now dynamically configure the settings for dark-mode availability, forced dark-mode, and theme switcher visibility at runtime.
- Dynamically configure sidebar width - you can now dynamically configure the width of the Filament sidebar using closures at runtime.
Final thoughts
Needless to say, the team is thrilled with this release! Tons of awesome features and fixes built on top of a massively impactful performance refactor, all bundled up for you in a non-breaking minor release.
Thank you again to the community, especially those who took the time to help us test out these performance fixes while they were still in beta! Without all of your help, we wouldn't have been able to get the results that we did while keeping this release as simple as bumping to the next minor version.