Mastering the Matrix field is a rite of passage for any Craft CMS developer. While these fields are incredibly flexible for content creators, developers often face the challenge of needing to group, count, or filter specific block types within a single field. Whether you are building a complex landing page with multiple layout sections or a tabbed interface where specific content needs to be isolated, knowing how to filter Matrix blocks by type is essential.
In this guide, you will learn the most efficient ways to query specific Matrix block types, how to handle version-specific syntax changes in Craft 4 and 5, and how to optimize your templates for better performance.
The Core Concept: Using the .type() Parameter
By default, when you access a Matrix field in your Twig templates (e.g., entry.myMatrixField), Craft returns an Element Query. To narrow this query down to a specific block type, you use the .type() method. This method accepts the handle of the block type you defined in the Craft Control Panel.
Here is the basic syntax to loop through blocks of a specific type:
{# Filtering for a single block type #}
{% for block in entry.myMatrixField.type('imageGallery').all() %}
<div class="gallery">
{{ block.image.one().getImg() }}
</div>
{% endfor %}
In this example, only blocks with the handle imageGallery will be returned. This is much cleaner and more performant than looping through every block and using an {% if %} statement to check the type inside the loop.
Handling Version Differences (Craft 3 vs. Craft 4 & 5)
One of the most common points of confusion for developers is why certain code snippets work in one project but fail in another. This usually comes down to how Craft handles element queries across versions.
Craft 3 Syntax
In older versions of Craft, you could often iterate over a field directly, though it was still best practice to use .all().
Craft 4 and 5 Syntax
In Craft 4 and Craft 5, you must use the .all() method to execute the query and fetch the results. If you omit .all(), you are trying to loop over the Query object itself rather than the resulting array of blocks, which will lead to unexpected behavior or errors.
{# Correct approach for modern Craft versions #}
{% set specificBlocks = entry.myMatrixField.type('textBlock').all() %}
Note for Craft 5 users: In Craft 5, Matrix blocks have been unified with Entries. While the .type() parameter still works, it is technically filtering by the Entry Type assigned to that Matrix block. The logic remains the same, but the underlying architecture is now even more powerful.
Counting Blocks for Conditional Logic
A frequent requirement is to only render a container <div> or a section wrapper if blocks of a certain type actually exist. You don't want empty HTML markup cluttering your DOM if the content creator hasn't added a specific block.
To achieve this, you can use the Twig length filter on your query results:
{% set featureBlocks = entry.myMatrixField.type('featureCard').all() %}
{% if featureBlocks | length %}
<section class="features-grid">
{% for block in featureBlocks %}
<div class="card">
<h3>{{ block.heading }}</h3>
</div>
{% endfor %}
</section>
{% endif %}
By setting the result of the query to a variable (featureBlocks), you avoid running the database query twice—once for the count and once for the loop.
Filtering Multiple Block Types at Once
Sometimes you need to fetch a subset of blocks that includes more than one type. The .type() method is flexible and can accept an array of handles or a comma-separated string.
{# Fetching multiple types using an array #}
{% set contentBlocks = entry.myMatrixField.type(['textBlock', 'imageBlock', 'videoBlock']).all() %}
{# Fetching multiple types using a string #}
{% set contentBlocks = entry.myMatrixField.type('textBlock, imageBlock').all() %}
This is particularly useful when you want to separate "content" blocks from "sidebar" or "utility" blocks within the same Matrix field.
Performance Optimization: Eager Loading
When filtering Matrix blocks, it is easy to fall into the "N+1" query trap, especially if your blocks contain assets or entries. If you are looping through entries on an index page and then filtering Matrix blocks inside each entry, your database activity will spike.
To prevent this, use Eager Loading. This tells Craft to fetch all the Matrix blocks (and their sub-elements) in a single query.
{# Eager loading the Matrix field in your main entry query #}
{% set entries = craft.entries()
.section('news')
.with(['myMatrixField'])
.all() %}
{% for entry in entries %}
{# Now filtering happens in memory, which is much faster #}
{% set images = entry.myMatrixField.type('image') %}
{# ... #}
{% endfor %}
Common Mistakes to Avoid
- Forgetting .all(): As mentioned, this is the #1 cause of bugs in Craft 4+ templates. Always execute your query.
- Using Type IDs instead of Handles: While you can use IDs, handles (like
textBlock) are much more readable and won't change if you migrate your database between environments. - Over-filtering in Twig: If you find yourself filtering a single Matrix field five different ways, consider if those should actually be separate fields or if your content model needs simplification.
- Checking for
null: A Matrix query will return an empty array if no blocks are found, notnull. Always use|lengthor{% if blocks is not empty %}.
Frequently Asked Questions
Can I filter Matrix blocks by a field value inside the block?
Yes! Just like any other element query, you can chain parameters. For example, entry.myMatrixField.type('textBlock').myCustomField('value').all() would find all text blocks where myCustomField matches 'value'.
How do I get only the first block of a certain type?
Instead of .all(), use .one(). For example: {% set firstVideo = entry.myMatrixField.type('video').one() %}. This is great for hero sections where only the first instance matters.
Does filtering by type affect the order of blocks?
No, the blocks will still be returned in the order they appear in the Control Panel (their sortOrder), unless you explicitly add an .orderBy() parameter to your query.
Wrapping Up
Filtering Matrix blocks by type is a powerful technique that allows you to treat a single Matrix field as a multi-purpose content store. By using the .type() method combined with .all() and the length filter, you can create clean, conditional layouts that respond dynamically to the content provided by your users.
Remember to always keep performance in mind by eager loading your fields when working with lists of entries, and you'll ensure your Craft CMS site remains fast and maintainable.