TLDR - Begining with Grav 1.5.10 and Grav 1.6.0, the Deferred Twig extension is available and an update to your theme is strongly recommended to ensure you won't have issues with plugins going forward.
What started as a Grav 1.6 only feature, has been back-ported in to Grav 1.5.10, as it's becoming more and more important to solve long-term issues with plugins that have their own CSS and JS assets. We have included support for the Twig Deferred Extension inside Grav which solves a rather annoying issue in Twig where content is rendered in the order they are encountered, and once rendered, cannot be manipulated. The best example of this limitiation has been in regards to CSS and JS assets added to the Grav Asset Manager, and generally rendered in the <head></head>
HTML block. If a an included Twig template, or a modular page, or even a plugin is processed after this block has rendered, they have no way of adding their own assets to the block.
The solution up to this point was to ensure that any assets were added prior to the Twig rendering, but often this is not a viable solution. An example of the problem is with Form fields that have specific CSS or JS requirements, and only attempt to add those assets when the field is rendered, long after the assets rendering in the <head></head>
tag is complete. We worked aroud this, by recommending a new bottom
asset block be added to the end of the Theme, so these assets can at least be rendered last.
Base Twig Template Changes
With regards to the Asset handling, a typical theme has usually been setup in this rough structure with the <head></head>
assets being rendered in each of the asset blocks of partials/base.html.twig
:
<!DOCTYPE html>
<html>
<head>
...
{% block stylesheets %}
{% do assets.addCss('theme://css/site.css') %}
{{ assets.css()|raw }}
{% endblock %}
{% block javascripts %}
{% do assets.addJs('theme://js/site.js', {group:'bottom'}) %}
{{ assets.js()|raw }}
{% endblock %}
...
</head>
<body>
...
{% block bottom %}
{{ assets.js('bottom')|raw }}
{% endblock %}
</body>
</html>
However, with the benefit of the deferred block extention, we can improve this dramatically with only a minor change. We simply move the rendering for both CSS and JS out of their respective blocks into a new assets deferred
block below.
<!DOCTYPE html>
<html>
<head>
...
{% block stylesheets %}
{% do assets.addCss('theme://css/site.css') %}
{% endblock %}
{% block javascripts %}
{% do assets.addJs('theme://js/site.js', {group:'bottom'}) %}
{% endblock %}
{% block assets deferred %}
{{ assets.css()|raw }}
{{ assets.js()|raw }}
{% endblock %}
...
</head>
<body>
...
{% block bottom %}
{{ assets.js('bottom')|raw }}
{% endblock %}
</body>
</html>
This simple change allows us to add CSS and JS from anywhere without having to resort to forcing the use of the bottom JS render block. That still serves a purpose of course, as often you do really want the JS to be rendered at the end of the page, however, CSS is supposed to be referenced in the <head></head>
, and up to this point, that has simply not been possible.
The {% block bottom %}...{% endblock %}
Twig block is also very important for plugins such as Form that rely on JavaScript be output at the bottom of the page. Please make sure all your themes have this consistent structure
Blueprints Grav Version Dependency
If you make your theme available via GPM, and for backwards compatibility reasons, it's also recommended that you set a minimum Grav requirement of 1.5.10+
in your theme's blueprints.yaml
:
...
keywords: quark, spectre, theme, core, modern, fast, responsive, html5, css3
bugs: https://github.com/getgrav/grav-theme-quark/issues
license: MIT
dependencies:
- { name: grav, version: '>=1.5.10' }
A full example of this change can be seen in this Quark theme commit
These changes are important for 3rd party plugins, and will be useful for Grav development as a whole going forward, so it's strongly recommended to update your themes to make use of this functionality. We will be updating all the core Grav, RocketTheme, and Trilby themes with this change very soon.
There's also other benefits to using deferred
for other Twig blocks as they work in the same way as the assets example already provided. The rendering of these blocks will be deferred to the end of the rendering process, so you can override them, or manipulate them after they would have been traditionally rendered.