Understanding deferred on twig blocks

In theory, I understand the purpose of adding deferred on the twig blocks. I’ve read the release and also the documentation under Asset Manager.

In practice, though, I’m not seeing any difference and I think I have to be missing something. I will use the Quark theme as an example.

In base.html.twig, there’s this block:

{% block assets deferred %}
    {{ assets.css()|raw }}
    {{ assets.js()|raw }}
{% endblock %}

From reading the documentation, it sounds like this means I can add assets in templates that extend base.html.twig, such as {% do assets.addCss('theme://css/my-custom.css') %}, and it will be included in the rendering in the assets block because its rendering is “deferred”.

However, I’m finding that it doesn’t matter if that block has deferred. In blog.html.twig, there’s a block:

{% block stylesheets %}
    {% do assets.addCss('theme://css/bricklayer.css') %}
    {{ parent() }}
{% endblock %}

But the output is the same whether I include deferred on base's assets block. I believe this is because it’s extending base's stylesheets block. It adds bricklayer.css then includes base's content via parent().

I’m also finding it unnecessary to wrap the do call inside of the stylesheets block. By remove that block in blog.html.twig and replacing it with just a simple

{% do assets.addCss('theme://css/bricklayer.css') %}

… and it still renders all the css correctly. This doesn’t surprise me because I would expect the {% block assets deferred %} to render at the end of the cycle. But I can remove deferred and again, everything works totally as expected.

Where I do see it working as I would expect is in partials/hero.html.twig. This partials is included via the include twig function. If assets is deferred, adding {% do assets.addCss('theme://css/my-custom.css') %} works. However, it won’t be included when I remove deferred. This is expected and is how I understand its purpose. But only in the included file, blog.html.twig still renders assets with/without deferred.

Then, when creating a modular hero page (which uses the same partial as above), the assets are not included. I’ve encountered this before in previous Grav versions, but it sounds like the magic of deferred should fix the problem of being able to add assets inside of a modular page.

In summary, my first [simple] question/verification is, is it necessary to include/extend the entire stylesheets block in blog.html.twig? Couldn’t that be replaced with a simple single do call and include the priority if I wanted?

The second part being, does the deferred flag only resolve problems of files included in templates that extend a template that includes a deferred block? Can someone give me another example of where deferred is necessary for things to render correctly?

If you read all of this, thank you!!

If you have a template that extends partials/base.html.twig then yes, you can of course, override the stylesheets block and there you can add a specific asset (bricklayer.css in your example). This has really been the only way to add assets to the top stylesheets block.

However, the bigger issue arises when you want to add an asset from a partial that does not extend the base. You can’t override a block that you are not extending. What if you are trying to add an asset from content where the twig is not rendered until page.content() is called, and that happens after the stylesheet block is rendered. Again, you aren’t extending the base, so you can’t override it.

The solution here has been to add it inline, or in the case of JS, add it to the bottom JS block. We were doing this as a workaround really. If you don’t have a bottom block, the asset wouldn’t load though, but it’s also restricting because while you can add JS at the bottom, CSS is supposed to be loaded in the head.

The deferred option allows you to add assets to the top and bottom block, whenever you like.

So I think you pretty much have the gist. Regarding your question about blog.html.twig, the old way works, but you could simply do a direct asset.add call and it would still work. There’s really not a great deal of difference between the two at this point, just more options.

1 Like

As always, thank you for your support and work Andy!

I think I understand it better from your explanation. Am I right in saying that attempting to include assets inside of a modular page still won’t be included (deferred or not)?

If I have a slideshow modular page and I am wanting to include slideshow.js inside the modular template, my js won’t get included in the assets.js() call?

they will be included if you have never_cache_twig: true set in the frontmatter of the modular page that is adding the asset.

Wait, are you saying that bricklayer.css loaded correctly without the stylesheets override block and without the deferred assets block?

Great question anyway, even if I lost track slightly after this. That’s only because I lack experience with deferred assets right now.

Awesome! Thanks again

I think the reason for this is because blog.html.twig directly extends base.html.twig. Andy was saying that deferred helps when a template file doesn’t directly extend the template that does the assets.js() call.

Definitely a little tricky to wrap your mind around it, though.

And you can either “extend” the stylesheets block (making sure to call {{ parent() }}) or directly use the assets.add() functions – just a personal preference. I think extending the block is more verbose but easier to understand what’s happening.

I’m still learning too!

Does still only work with never_cache_twig: true? Or is there an alternative to that? I would be interested in adding css files in modular templates.