How to speed up a single entry page with Eager Loading
Disclaimer: While eager-loading will almost always bring down the total number of queries executed, it's important to note that having less queries won't always equal better performance. When optimising a page with eager-loading, you should take care to observe the actual time Craft spends on database queries – which is the metric that matters.

For Craft 3, you should enable the Debug Toolbar, which will tell you exactly how many milliseconds Craft is using for database queries. Eager loading is not a silver bullet, and you should note the time spent on database queries with and without eager loading, to determine if it's actually increasing performance or not. You can enable the debug toolbar from your Craft user's profile page inside the Control Panel.

For Craft 2, you'll need to enable devMode, crack open your browser's dev tools console and expand the *Profiling Summary Report*. Unfortunately, AFAIK there's no record of the time spent on queries specifically, but you can gauge it by looking at the Time property (which will report the total time Craft spent resolving the request, including database queries). Obviously, the lower this number is, the better.

Furthermore, before worrying about eager loading, you should make sure that your template is properly optimised without it. The most important thing is to make sure that you're not doing multiple, redundant queries.

In the code example above, you're first executing two queries on the entry.imageGallery() field outside the {% for %} loop, and then 1 additional query per image inside the {% for %} loop. In other words, if you added 100 images to the galleryImages field, you'd execute a total of 102 queries – which is totally unnecessary.

By caching the array of Assets returned by .all() to a variable, you can get away with a single query:

{# Get all images in the field #}
{% set galleryImages = entry.galleryImages.all() %}

{# Set the `titleImage` to the first image #}
{% set titleImage = galleryImages[0] ?? null %}

{% if titleImage %}
    ...
{% endif %}

{{ entry.bodyText }}

{# Use the |slice filter to remove the first image – i.e. the title image – from the Assets array, and then print the rest of the images #}
{% set galleryImages = galleryImages|slice(1) %}

{% if galleryImages|length %}
    {% for image in galleryImages %}
        ...
    {% endfor %}
{% endif %}


Note the use of the |slice filter – this is used to remove the first index (i.e. the first image) from the array, after caching it to a variable titleImage. Don't worry, the |slice operation won't throw an error, even if the galleryImages array is empty.

With the above optimisation, your X number of queries have been reduced to a single query – and at that point, you probably wouldn't see any performance improvements by adding eager loading.

But, if you wanted to try it – here's how it could be done (this would replace the first line in the above example):

{% do craft.app.elements.eagerLoadElements(
    className(entry),
    [entry],
    ['galleryImages']
) %}


The above statement should go at the very top of your template – and if you do this, make sure to remove the reference to .all() in the first line of the first example (by eager loading the Assets field, the entry.galleryImages property is already an array and hence has no all() method).

Again, the above is probably not useful if you do the optimisations to bring down your query count to 1 – but, it could be useful if you had multiple relational fields you wanted to query, i.e.

{% do craft.app.elements.eagerLoadElements(
    className(entry),
    [entry],
    ['galleryImages', 'entriesField', 'categoryField']
) %}


For more info on eager loading, check out this excellent article.