Faster Laravel: Surprising Optimization Tips

Published on by

Faster Laravel: Surprising Optimization Tips image

When we started working on our test management tool Testmo a few years ago and selected Laravel for our backend implementation, there were a couple of surprising performance issues we've noticed.

For Testmo our goal is to answer most server requests in 100ms or less. You might be wondering why this is important to us when most apps spend significantly more time rendering the frontend in the browser and executing JavaScript (which is also true for Testmo, especially with our complex UI for test case management with multiple panes and dynamic folders).

It's pretty simple: slow server-side performance will trickle down to all parts of the stack, so optimizing this part will have very positive effects on the entire app. Testmo customers are also heavily using our API as part of our test automation features. So optimizing the server-side performance also helps us to significantly reduce server load to better scale the app with fewer servers & resources.

You can find many generic Laravel optimization articles out there describing the basics of configuring caching, how to build faster database queries etc. Instead, in this article I want to share three specific optimizations for Laravel Blade views that surprised us when we started using Laravel and that resulted in drastic performance improvements.

Blade loops can be surprisingly slow

A significant part of server-side performance is based on how fast you can transform database query results to rendered content. In Laravel's case you would usually use Blade views to render your content and send results to browsers. When we initially started working with Blade and its various loop directives, we were surprised that loops are quite slow. The reason for this is that they often provide additional features that might be useful in some cases, but are unnecessary slow when you don't need them.

Let's look at Laravel Blade's @foreach loop directive as an example. You would use @foreach very often in views to iterate over data and render content, so its performance is quite critical. When we started using Laravel and measured the view performance, this directive was surprisingly slow. We expected that it maps more or less directly to PHP's foreach loops, but when you look at Laravel's implementation, it adds a lot of overhead to add the loop variable.

This feature might be useful from time to time, but it adds significant overhead in the majority of cases you don't need it. The solution for us was quite simple: we don't use @foreachat all, but added our own @loop directive, which directly maps to PHP's native foreach:

Blade::directive('loop', function ($expression) {
return "<?php foreach ($expression): ?>";
Blade::directive('endloop', function ($expression) {
return "<?php endforeach; ?>";

Don't include & render many views

Views are a great way to reuse common UI elements in different parts of the app without repeating your frontend code. Let's say you are rendering the avatar of users in the UI as part of various data tables, sidebar sections and in other places. Similar to how we render user avatars to indicate contributors and owners of exploratory testing sessions in Testmo:

Ideally we could use regular Blade views to reuse and include such common UI elements, as rendering the avatar takes >10 lines of code (e.g. to fallback to user initials for users who haven't uploaded an avatar yet). But it turns out that using many Blade views in a request is a very bad idea, as including views can be very slow. So slow in fact that rendering larger tables with many UI elements can add 100s of milliseconds of overhead, which would be unacceptable for most apps.

For Testmo we try to keep the number of rendered views per request to less than 15 on most pages. This unfortunately rules out using views for common elements that could be rendered dozens of times. Instead, we've come up with an alternative approach with a good balance of dev productivity & app performance, a concept we call partials internally.

Partials are pre-rendered views that we compile & store during development and directly inject into views via a simple blade directive. This way we can easily reuse common UI elements and only write them once without the overhead of including full views. Instead, our internal @partial blade directive loads the pre-rendered code and outputs it in-place. So our views can load partials similar to including a full view, but when you look at the compiled & cached view output, it directly includes the pre-rendered code without any overhead.

@partial('avatars.user', [
'user' => $user,
'size' => 'table',
'tooltip' => $user->name

Including views is still slow & error-prone

Using partials instead of views was a big performance win, but including views was still unreasonable slow. But there was another issue we didn't like about Blade's default @include behavior: included views automatically inherit all data available in the parent view. So it becomes difficult to control which parameters are passed to views, which can lead to bugs and unexpected behavior quickly if you are not careful.

We noticed quite quickly that we didn't like this default behavior, so we looked into ways to improve this. When we reviewed Laravel's default @include implementation, we noticed that changing this behavior also had the potential for significant speed improvements.

In Testmo we don't use @include at all anymore. Instead, we've built our own lightweight alternative we call @require. Our own directive doesn't make all parameters available in sub views anymore, which makes it easier for us to avoid bugs, improves testability and helps us quickly understand which parameters are used & available. It also avoids using PHP's get_defined_vars function, which causes a lot of the performance issues with Laravel's default @include implementation.

// Not automatically available in sub view
$completed = true;
@require('automation.results.header', [
'results' => $results,
'states' => $states,

The three above mentioned optimizations combined resulted in huge performance differences for non-trivial pages (sometimes 100s of milliseconds). Laravel's default behavior and Blade implementation provides a lot of useful features. If you don't need all these features and prefer faster views, it can make sense to use simpler & faster implementations though. Hopefully this article gives you some ideas on how to optimize your own Laravel apps.

This guest posting was written by Dennis Gurock, one of the founders of Testmo. Testmo is built using Laravel and helps teams manage all their software tests in one modern platform. If you are not familiar with QA tools, Testmo recently published various tool guides to get started:

Eric L. Barnes photo

Eric is the creator of Laravel News and has been covering Laravel since 2012.

Filed in:

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.


Version 4 of Tinkerwell is available now. Get the most popular PHP scratchpad with all its new features and simplify your development workflow today.

Visit Tinkerwell
Laravel Forge logo

Laravel Forge

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Forge
Tinkerwell logo


The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

No Compromises logo

No Compromises

Joel and Aaron, the two seasoned devs from the No Compromises podcast, are now available to hire for your Laravel project. ⬧ Flat rate of $7500/mo. ⬧ No lengthy sales process. ⬧ No contracts. ⬧ 100% money back guarantee.

No Compromises
Kirschbaum logo


Providing innovation and stability to ensure your web application succeeds.

Shift logo


Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Bacancy logo


Supercharge your project with a seasoned Laravel developer with 4-6 years of experience for just $2500/month. Get 160 hours of dedicated expertise & a risk-free 15-day trial. Schedule a call now!

Lucky Media logo

Lucky Media

Bespoke software solutions built for your business. We ♥ Laravel

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel E-Commerce

E-Commerce for Laravel. An open-source package that brings the power of modern headless e-commerce functionality to Laravel.

Lunar: Laravel E-Commerce
LaraJobs logo


The official Laravel job board

Larafast: Laravel SaaS Starter Kit logo

Larafast: Laravel SaaS Starter Kit

Larafast is a Laravel SaaS Starter Kit with ready-to-go features for Payments, Auth, Admin, Blog, SEO, and beautiful themes. Available with Vue and Livewire stacks.

Larafast: Laravel SaaS Starter Kit
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit
Rector logo


Your partner for seamless Laravel upgrades, cutting costs, and accelerating innovation for successful companies


The latest

View all →
Asserting a JSON Response Structure in Laravel image

Asserting a JSON Response Structure in Laravel

Read article
Backpack turns 8 years old, celebrates with 40% discount image

Backpack turns 8 years old, celebrates with 40% discount

Read article
Create a DateTime from a Timestamp With this New Method Coming to PHP 8.4 image

Create a DateTime from a Timestamp With this New Method Coming to PHP 8.4

Read article
Neovim Plugin to for Navigating Laravel and Livewire Components image

Neovim Plugin to for Navigating Laravel and Livewire Components

Read article
Laravel Herd v1.7 is out with updates to the dump UI image

Laravel Herd v1.7 is out with updates to the dump UI

Read article
Share Error Package for Laravel's New Exception Page image

Share Error Package for Laravel's New Exception Page

Read article