We use cookies to make your viewing experience better. By accepting you consent, you agree to our Cookie policy
Looping over entries is a cornerstone of Craft CMS development. But bottlenecks, complex queries, and pagination bugs can quickly frustrate. This guide provides pro tips for smooth, optimized entry loops. Learn how these iterations work under the hood, streamline performance, output templates fast, and troubleshoot paginated archives, product listings, and more. Everything you need to master entry loops in Craft.
Entry loops iterate over Craft entries to output content. Core features include template syntax like {% for %} tags, eager loading to optimize queries, and integration with pagination and caching. Loops enable displaying entries on indexes, archives, calendars, and product pages. Techniques like nested loops and custom filters enhance capabilities. Entry loops are tailored for Section entries versus categories, assets, and other elements.
Entry loops are a powerful element in Craft CMS that allow you to iterate through collections of entries and output them in templates. At their core, entry loops work by defining a loop that will cycle through a set of entries, usually defined by an entry query.
Within the loop, you can output fields from each entry in the collection using Twig syntax like entry.title and entry.body. This makes it easy to create lists of blog posts, projects, team members etc. The key difference from other types of loops is that entry loops work specifically with entries from Sections in Craft.
Compared to looping over a simple array or list, entry loops provide more structure and features for working with groups of entries. For example, you can eager load related elements to avoid extra queries. And entry models give you access to helpful methods for outputting data, links, and more. Overall, entry loops are optimized for the type of collections, structures and outputs you commonly need when working with entries in Craft CMS.
The primary Twig syntax used for entry loops is the {% for %} tag. This defines the iteration and lets you set variables like entry to reference each item in the loop:
{% for entry in entries %}
{{ entry.title }}
{% endfor %}
The entries variable would normally come from an entry query, which selects which entries should be looped through. Additional Twig filters can be chained to the query to further manipulate the entries.
Inside the loop, dot notation like entry.title accesses fields on the entry models. Entry models provide various display methods to output content in templates, like entry.summary(), entry.getLink(), and entry.postDate.format('F Y') for formatting dates.
For optimal performance, eager loading can be used to reduce database queries, by eager loading any related elements upfront:
{% set entries = craft.entries()
.with(['relatedEntries'])
.all() %}
This avoids N+1 query issues in large entry loops. Overall the Twig template syntax provides a robust way to iterate over entry collections in Craft CMS.
While entry loops share some similarities with other loop types, there are a few key differences that set them apart:
Category Loops
Category loops output a structure of categories and sub-categories. Entry loops work with flat collections of entries.
Asset Loops
Asset loops iterate over files and images. Entry loops work with Section entries that contain content across multiple fields.
Global Loops
Global loops output fields from global sets. Entry loops pull content from structured entry models.
Element Queries
Element queries can loop over multiple element types. Entry loops focus just on entries from Sections.
The specific focus of entry loops on Collections and Section entries makes them optimized for powering common templates like blog indexes, team pages, case study listings, and similar entry-focused templates in Craft CMS.
The most common way to use entry loops is to display content from a Section. The loop iterates over the entries, allowing you to output fields, titles, images and more.
To start, you create an entries query for the Section, usually filtered by criteria like entry type, status, order etc. Common filters are section, type, status, and order:
{% set entries = craft.entries()
.section('news')
.type('article')
.status(true)
.orderBy('postDate desc')
.all() %}
The query can then be looped through using a for tag:
{% for entry in entries %}
<h2>{{ entry.title }}</h2>
{{ entry.summary }}
{% endfor %}
Inside the loop entry will be set to each entry model, allowing you to output fields using dot notation like entry.title.
Pagination can be added by limiting and offsetting the query results. And caching can optimize performance for large entry collections.
Overall, it provides a straightforward way to display content from multiple entries.
For more complex templates, advanced entry loop techniques open up further possibilities:
Nested Loops
Loops can be nested to output hierarchical or related data. For example, looping over categories to output entries within each category.
Mixed Element Loops
Entry loops can be combined with loops over categories, assets or other elements to create mixed output.
Eager Loading
Eager loading improves performance by reducing database queries for related elements.
Custom Filters/Modifiers
New Twig filters can transform or customize entry output within loops.
Debugging Loops
Debugging tools like dump(), source() and debug bars help diagnose loop issues.
These approaches unlock more powerful use cases when working with entry collections.
Within an entry loop, Twig gives you full access to display content from each entry:
Titles
Entry titles can be output using {{ entry.title }}.
Summaries
The entry.summary() method outputs a summary from the full body content.
Body Content
{{ entry.body }} outputs the full body content field.
Images
Images can be output using {% set image = entry.image.one() %} and {{ image.url }}
Dates
Dates are accessible via entry.postDate and can be formatted using Twig's date() filter.
Links
entry.getLink() generates a URL to the entry page.
Combined together, these allow you to fully output SEO-friendly templates optimized to display entry content.
There are various techniques that can optimize entry loop speed:
Eager Loading
Eager loading related elements avoids N+1 query issues:
{% set entries = entries.with(['relatedEntries']) %}
.find() vs .all()
.find() iterates without loading all entries, .all() loads all upfront.
Avoiding N+1 Queries
Fetch related data upfront with eager loading.
Caching
Cache full entry query results for repeated loops.
Deferred Filtering
Filter early to narrow down results.
Pagination
Paginate long lists into smaller chunks.
Minify Templates
Minify Twig templates to reduce parse time.
Following these best practices will improve entry loop speed.
To diagnose entry loop problems:
Enable Debugging
Craft's debugger bar shows execution time and queries.
Profile Pages
Browser profiling tools pinpoint slow sections.
Log Statements
Log statements can time portions of a loop.
Examine Queries
Check for N+1 queries using eager loading.
Identify Bottlenecks
Find where time is spent - DB, code, cache etc.
Simulate Traffic
Test under simulated production traffic.
Targeted diagnostics make it possible to pinpoint and fix entry loop issues.
Caching and pagination help optimize long entry loops:
Full Page Caching
Cache the full HTML to avoid running queries.
Fragment Caching
Cache just the entry loop HTML fragment.
Invalidate Selectively
Invalidate strategically on entry changes.
Pagination
Break long lists into smaller pages.
Limit Offset
Paginate by limiting and offsetting the entry query.
Page Caching
Cache each paginated page separately.
For maximal optimization, combine caching strategies with pagination to improve performance.
Careful cache invalidation avoids stale data.
Together, these approaches help ensure fast, efficient entry loops - even for large, complex queries across thousands of entries. By following performance best practices, diagnosing issues, and leveraging caching and pagination, entry loops can deliver lightning-fast experiences for Craft CMS sites.
Debugging entry loops employs several helpful techniques:
Log Statements
Logging variables shows their values during execution.
var_dump()
Dumps structured information about a variable for debugging.
Debugger Toolbar
Craft's debug toolbar provides execution times, queries, and profiling.
Error Messages
Read error messages carefully to pinpoint causes.
Common Problems
Check for undefined variables, indexing issues, cache interference etc.
Targeted debugging provides visibility into common entry loop problems.
A common issue is missing data from related elements. This happens when lazy loading related elements:
{% for entry in entries %}
{# Bad - lazy loads categories #}
{{ entry.categories.title }}
{% endfor %}
This results in N+1 queries to load the related categories each iteration.
Eager loading solves this:
{% set entries = entries.with(['categories']) %}
{% for entry in entries %}
{# Good - eager loaded categories #}
{{ entry.categories.first().title }}
{% endfor %}
Now categories are queried just once upfront. Always eager load related elements that are output in loops.
More complex entry loops often involve pagination and caching:
Pagination Inconsistencies
Debug unexpected page counts, item numbers etc.
Expired Cache
Check for expired cache causing empty, stale data.
Cache Key Issues
Validate cache keys are unique for each query.
Complex Query Problems
Isolate issues with complex criteria, custom fields etc.
N+1 Queries
Eager load all relations needed for paginated loops.
A systematic approach helps uncover the source of pagination and caching bugs.
Careful debugging provides the insights needed to fix frustrating entry loop issues. Tracking down problems like N+1 queries, caching, pagination, and eager loading relations allows you to optimize entry loop performance and stability. With the right techniques, you can troubleshoot and resolve even subtle, intermittent entry loop bugs.
Entry loops are perfect for powering blog templates:
Blog Index
Loop through blog post entries, sorted by date. Output titles, summaries and thumbnails.
Category/Tag Archives
Filter by categories or tags to show filtered archive lists.
Recent Posts
Limit to show just the 10 most recent posted blog entries.
Post Sidebars
Loop over 4 related entries to show in a blog post sidebar.
Paginated Indexes
Paginate the index into multiple pages using .limit() and .offset()
Post Excerpts
Output post excerpt text from the entry.excerpt field in summary views.
For blogs, entry loops provide the flexibility to show posts in all required ways.
Entry loops are useful for events:
Upcoming Events
Limit to show upcoming event entries sorted by date.
Event Listings
Loop over all published event entries.
Grouped by Date
Group event entries by date into daily/weekly/monthly layouts.
Filtered Events
Allow filtering events by category, date period, location etc.
Event Calendars
Output events into a calendar view by mapping dates to days.
Featured Events
Limit to highlight featured event entries specifically.
Related Events
Show related event entries at the bottom of each event.
The structure of entries makes them fit naturally for displaying events.
Product and service templates can also leverage entry loops:
New Arrivals
Limit to show the latest products by release date.
Category Pages
Loop over products within a certain category.
Sale Items
Filter products on sale using custom sale field criteria.
Featured Products
Limit products marked as featured.
Recommendations
Create a related/recommended product block using a loop.
Comparison Tables
Loop over products to populate a comparison table.
Product Carousels
Cycle product entries in a carousel component.
Entry loops provide the query power needed for robust product/service templates.
The structured content model of entries lends itself well to looping over blogs, events, products, and many other types of content in Craft CMS.
While entry loops are very useful, there are some cases where alternate approaches may be better:
Single Entries
No need to loop if displaying just a single entry. Retrieve it directly instead.
Global Queries
For global content, use global sets instead of single entry Sections.
Entry Queries
For simple lists, an entry query may suffice without a full loop.
Eager-Loaded Entries
If entries are already eager loaded, iterate them directly.
Non-Display Usage
For background tasks, avoid looping when not displaying data.
Simpler Data
For non-structured data, arrays or objects may suffice.
Evaluating the specific use case can reveal if a simpler approach is more optimal.
Entry loops specifically target entry collections. For other elements, different approaches are needed:
Categories
Use craft.categories to fetch category models directly.
Assets
Loop over assets using craft.assets and asset queries.
Users
Retrieve users with craft.users and user queries.
Fetch matrix blocks using entry.matrixFieldHandle on entries.
Tags
Loop over tags using entry.tags rather than standalone queries.
Custom Tables
For custom tables, use custom services and models.So while entry loops are specialized for entries, they don't apply universally to all content types.The goal is to always match the loop type to the models and data structures you are outputting.
Entry loops are optimized for Section entries, but aren't necessarily the best solution for categories, users, and other non-entry element types.
Considering the specific elements needed for a template can help identify when to reach for alternate query and loop techniques instead of traditional entry loops in Craft CMS.
Andy has scaled multiple businesses and is a big believer in Craft CMS as a tool that benefits both Designer, Developer and Client.