We use cookies to make your viewing experience better. By accepting you consent, you agree to our Cookie policy

Accept
Improve your Craft CMS skills

A Guide To Craft CMS Eager Loading

10 min read
Shape April 2022 HR 45

Eager loading is a critical yet often overlooked optimization in Craft CMS. By preloading related content upfront, eager loading eliminates the N+1 query bottleneck that cripples site performance. This guide will demonstrate exactly how to implement eager loading in your Craft CMS templates to drastically cut page load times. You'll learn techniques for identifying when eager loading is needed, along with actionable steps to enable it for entries, assets, and other elements. Read on to master this vital performance tweak.

Eager loading is an optimization technique in Craft CMS that preloads related content upfront when querying elements, avoiding N+1 queries. By eagerly loading entries, assets, and other linked data, page load times can be drastically reduced. But caching and memory usage need to be tuned to maximize its performance impact.

Eager Loading in Craft CMS

Defining Eager Loading

Eager loading is a technique in Craft CMS that allows you to preload related content upfront instead of having it loaded separately later. This differs from the default lazy loading behavior, where related content is only loaded when specifically requested.

With eager loading, when you query for an entry, Craft will automatically eager load any related content you specify, avoiding the need for separate additional queries. This avoids the dreaded N+1 query problem that can seriously impact site performance.

To define it simply - eager loading prefetches relational data in one go rather than running a separate query each time you need to access the related elements. This avoids the multiplying number of queries that lazy loading triggers.

Why Eager Loading Matters for Performance

Eager loading is incredibly important for optimizing the performance of content-heavy Craft CMS sites. By eager loading related content upfront, you avoid the waterfall of N+1 queries that would otherwise happen.

For example, if you lazy load entries and then also need to load their related categories and tags, that turns 1 query into 3+ queries depending on how many entries are loaded. The more complex the relations between content, the more exponential the number of queries.

By eager loading the related categories and tags when querying for entries, you reduce this to just 1 or 2 queries no matter how much related data needs to be accessed. This significantly speeds up page load times.

On large sites with hundreds or thousands of content entries and complex relations, the performance difference can be staggering. Eager loading everything needed upfront makes the experience seamless while lazy loading creates a query and performance nightmare.

As a real world example, a website displaying 100 entries would normally require:

  • 1 query to get the 100 entries

  • 100 queries to load the related categories for each entry

  • 100 queries to load the related tags for each entry

That's 201 queries just to fully load 100 entries! With eager loading this becomes:

  • 1 query to eager load 100 entries, their categories, and their tags

By eager loading the related data, the number of queries is reduced by 200. And the more entries that are loaded, the bigger this multiplying performance impact becomes.

This clearly shows why eager loading is critical for performance optimization, especially on content-heavy Craft sites. It prevents an exponential slowdown as the relations between content increases.

Optimizing Eager Loading in Craft

When eager loading in Craft, you need to be strategic about what data you choose to load upfront. Eager loading everything can counterintuitively hurt performance if not done carefully.

Here are some tips for optimizing eager loading:

  • Only eager load relations that are definitely needed on the page. Don't preload data that may not get used.

  • Avoid nested eager loading more than 1 level deep. Craft's eager loading can cascade exponentially.

  • Use Craft's with() method for simple eager loading. For complex loading use Element Queries.

  • Test different eager loading strategies and measure for performance. More is not always better.

  • Balance eager loading with selective lazy loading for non-critical relations.

  • Use plugins like Nitro to identify N+1 queries that should be eager loaded.

Getting the right balance of eager vs lazy loading for your project takes trial and error. Measure page load times and database queries as you adjust your eager loading approach.

The key is to optimize it to load the critical related data upfront while avoiding overfetching. This will ensure maximum performance without creating new bottlenecks.

When to Eager Load Specific Relations

Deciding what relations to eager load vs lazy load depends on the context and how the data will be displayed.

However some common examples where eager loading is recommended:

Categories and Tags

Entries will often display their related categories and tags so these are good candidates for eager loading:

{% set entries = craft.entries()

.with(['relatedCategories', 'relatedTags'])

.all() %}

Entry Images

If entries will always show their banner image, eager load it:

{% set entries = craft.entries()

.with('bannerImage')

.all() %}

Navigation Menu Items

Eager load pages/entries/URLs for menu templates:

{% set items = craft.entries()

.section('menuItems')

.with(['relatedEntry', 'relatedPage', 'relatedUrl'])

.all() %}

Localized Content

If displaying translations eager load them:

{% set entries = craft.entries()

.site('*')

.with(['translations'])

.all() %}

These are just some common examples. Analyze relations on a template-by-template basis.

Eager loading is a vital optimization technique for Craft CMS. By preloading related data upfront, it avoids the waterfall of N+1 queries and drastically speeds up page loads.

Be strategic about what relations to eager load. Measure performance as you optimize. The effort required to properly implement eager loading is well worth the UX and performance gains it can provide.

Identifying When to Use Eager Loading

Templating Situations Needing Eager Loading

Certain templates and pages in Craft CMS will benefit from eager loading to avoid N+1 queries and optimise performance.

Here are the key scenarios where implementing eager loading is recommended:

Entry Listing & Archives

Templates that display multiple entries like blog listings, category archives, and author archives should eager load related entries data like tags, categories, assets etc.

Entry Detail Pages

Single entry templates should eager load the entry's related assets, categories, tags, author and any other linked elements that will be output.

Asset Galleries & Pages

Asset gallery and file listing templates should eager load related tags, categories, and linked entries. Single asset pages should eager load related data.

Tag & Category Browsing

Tag and category browsing templates should eager load the related entries to be displayed.

Global Search Results

Search results pages should eager load associated content like entry images, videos, related products etc.

Navigation Menus

Main site navigation templates should eager load any linked entries, pages or URLs.

Sitemaps & Feeds

Sitemap, RSS, Atom and JSON feed templates should eager load localised translations, related assets etc.

Multi-site Setups

On multi-site installs, eager load site-specific relations and translated versions of entries.

These templates display large amounts of relational data so optimising them with eager loading should be prioritised.

Identifying N+1 Queries

To identify templates suffering from N+1 query problems that can be fixed with eager loading, look for:

Slow Page Load Times

Use Chrome dev tools Network tab to spot slow loads. Check if large numbers of queries are responsible.

Duplicate Queries

Review dev tools or Craft logs to catch repeated queries fetching the same relations.

Exponential Query Growth

If the number of queries multiplies exponentially as more content loads, it likely indicates N+1 problems.

Diagnostic Tools

Use tools like Nitro or rrrova to detect specific N+1 queries.

Template Profiling

Enable Craft's template profiling to see all queries. Look for exponential query growth between pages.

Content Model Analysis

Review content models for complex relationships that often trigger lazy loading issues.

Log File Analysis

Dig into Craft and PHP logs to find duplicate, slow database queries indicative of N+1 problems.

Pinpointing exactly where lazy loading is hurting performance is key. Addressing these pain points with eager loading should be the top priority.

Understanding the Tradeoffs

While eager loading optimises queries, some tradeoffs and constraints come with it:

Memory Overhead

More data loaded upfront increases memory usage. This can become a bottleneck if over-eager loaded.

First Page Load Impact

The initial page load may be slower as more data is fetched but improves over time.

Caching Complexity

Caching has to be tuned to account for preloaded data. The cache overhead increases.

Overfetching

Eager loading data that actually isn't required wastes resources.

Scope Creep

It's easy to over-eager load relations. Resist temptation to load data that isn't critical.

Eager loading should be focused on high-impact relations where the reduction in queries offsets the tradeoffs. Avoid preloading unnecessary data that won't improve performance.

Test eager loading on a staged environment first. Measure page load times and monitor for any bottlenecks introduced. Adjust loading strategies based on evidence and data.

Balance eager loading on key templates with selective lazy loading on relations that aren't called often. Find the optimal middle ground.

The extra work required to implement eager loading properly is justified by the significant site speed and performance gains it enables if done right. But due diligence is required to minimise potential downsides.

Implementing Eager Loading in Craft CMS

Eager Loading Entries

Eager loading for entries retrieves related data upfront when querying for entries. Here are some examples:

{# Eager load entry categories #}

{% set entries = craft.entries()

.with(['relatedCategories'])

.all() %}


{# Eager load author and post image #}

{% set entries = craft.entries()

.with(['author', 'postImage'])

.all() %}

{# Customize related fields #}

{% set entries = craft.entries()

.with([

'relatedCategories' => ['limit' => 5],

'author' => ['select' => ['firstName', 'lastName']],

])

.all()

%}

You can eager load entries in conjunction with pagination:

{# Paginated eager loaded entries #}

{% paginate craft.entries()

.with(['categories, 'author'])

.limit(10) as pageInfo, pageEntries %}

{% for entry in pageEntries %}

{# output entry #}

{% endfor %}

This optimizes paginated queries by eager loading the related data only once.

Eager Loading Assets

Eager loading for assets preloads transforms, tags, and other linked elements:

{# Eager load asset transforms #}

{% set assets = craft.assets()

.with(['focusedImage'])

.all() %}


{# Eager load related tags #}

{% set assets = craft.assets()

.with(['relatedTags'])

.all() %}

{# Paginated eager loaded assets #}

{% paginate craft.assets()

.with(['relatedTags'])

.limit(24) as pageInfo, pageAssets %}

All asset transformations and tags are loaded upfront avoiding N+1 queries when outputting them in templates.

Eager Loading Other Elements

Eager loading works for categories, tags, globals, and other elements:

{# Categories and entries #}

{% set categories = craft.categories()

.with(['relatedEntries'])

.all() %}


{# Tags and related assets #}

{% set tags = craft.tags()

.with(['relatedAssets'])

.all() %}

{# Global sets and eager loaded fields #}

{% set globalSets = craft.globalSets()

.with(['copyrightNotice'])

.all() %}


Users can also be eager loaded:

{# Eager load user field data #}

{% set users = craft.users()

.with(['firstName', 'lastName'])

.all() %}

However, take care when eager loading users as there may be sensitivity concerns with exposing user data.

In summary, eager loading can be implemented for any element type in Craft CMS to solve N+1 query problems and optimize performance. Adjust the syntax based on the relations being loaded.

Caching and Performance Considerations

Caching Implications

Eager loading has implications for caching that need to be considered. More data is loaded upfront, so cache invalidation becomes more frequent as data changes.

Craft CMS provides a cache tagging system which can help optimise caching with eager loading. Cache tags are attached to elements and invalidate caches when changed.

Some strategies for efficient caching with eager loading:

  • Use cache tags to only invalidate caches when essential data changes. Avoid over-invalidating.

  • Tune cache lifetimes - longer for static content, shorter for eager loaded relations.

  • Cache data at different levels - page cache, element cache tags, fragment caching.

  • Profile cache hit rates to identify ineffective caches.

  • Implement a CDN and edge caching for eager loaded data.

  • Precache data during deployments using plugins like Bond.

  • Limit eager loading depth to avoid complex automatic cache invalidation.

Finding the optimal cache lifetimes and tagging strategies takes trial and error. Measure performance using profiling tools while adjusting configs.

Fine tuning caching is essential to maximise the performance gains of eager loading. When done right, the reduction in database queries will outweigh any potential cache overhead.

Craft CMS Profiling Techniques

To accurately measure the performance impact of implementing eager loading, leverage Craft's built-in profiling toolkit:

  • Enable Template Profiling in Craft settings to see every query.

  • Use {% cache %} tags to profile query count for cached fragments.

  • Review ** longtime ** in Craft's logs for slow queries.

  • Analyse Trace Logging for detailed database query profiles.

  • Toggle Dev Mode to enable deeper DevTools timeline profiling.

Third party tools like Xdebug and Clockwork also integrate nicely:

  • Xdebug profiling reports help identify bottlenecks.

  • Clockwork provides advanced analytics for optimisation.

  • XHProf/XHGui offer full callgraph profiling for PHP.

Proper profiling quantifies the before and after with eager loading implemented. Always measure on a staging environment first before rolling out changes.

Aim to reduce:

  • Overall number of queries

  • Duplicate queries for the same data

  • Time spent executing database queries

  • Time spent rendering templates

Review profiling reports for any new bottlenecks introduced. Eager loading reduces queries but can increase load or memory pressure. Continuously tune and adjust based on evidence from profiling.

Measure page load time from real user locations using tools like Lighthouse. Significant performance gains should be visible from real world browsing sessions.

Proper profiling is invaluable when implementing performance optimizations like eager loading. It provides data-driven guidance for tuning caching, load balancing, and other factors that impact success.

By following Craft best practices for profiling, you can accurately test and validate eager loading improvements on your site. The numbers don't lie - if done correctly, eager loading will result in measureable boosts in site performance.

Shape April 2022 HR 202
Andy Golpys
- Author

Andy has scaled multiple businesses and is a big believer in Craft CMS as a tool that benefits both Designer, Developer and Client. 

Share
Feedback
Show us some love
Email Us
We usually reply within 72 hours
Agency Directory
Submit your agency
Affiliate Partners
Let's chat