I’m trying to extend one of my themes by adding custom components using modular pages. The issue I’m facing is that I can’t load assets inside a module the same way I would in a regular page. Using:
{% do assets.addCss('theme://css/componentA.css') %}
{% do assets.addJs('theme://js/componentA.js', {group: 'bottom'}) %}
has no effect when placed inside a modular page template. Because of this, the only reliable option seems to be placing the CSS and JavaScript directly inside and blocks within the module file.
I remember reading that Grav has some limitations regarding asset loading in modular pages.
To keep my templates cleaner, I’m currently including partials that contain the CSS and JS:
{% include 'modular/partials/components/componentA/componentA-styles.html.twig' %}
{% include 'modular/partials/components/componentA/componentA-scripts.html.twig' %}
This works, but it feels like a workaround.
Is there a more efficient or recommended way to include CSS and JavaScript within a modular page module?
Quick shot off the top of my head as I remember having had that issue in the past: try checking “never cache twig” in the Advanced options of the module.
Oh, I remember this now…
Here’s what I have in one of my plugins README:
If you use Photoswipe in a module template, you must add this to the module’s (not the main modular page) frontmatter: never_cache_twig: true. You don’t need to do this if your site has caching disabled.
If caching is enabled and your Photoswipe gallery is invoked in a module template, all of the assets that make Photoswipe work are omitted from the cache. You will see the gallery unstyled and unscripted and lightbox will not work.
Ideally, the user shouldn’t have to depend on whether the cache is enabled or not. The option I mentioned earlier isn’t bad, because I keep the file with the Twig template code separate from the files that load the CSS and JS assets, but it would be ideal if the assets could be loaded in a modular page module, just like they are in non-modular templates.
Another option I considered would be to implement a toggle in the theme to enable or disable the extra component I want to add, and if it’s enabled, load it in the stylesheets block of the base file (or the one responsible for loading the theme’s assets).
Anyway, it seems I’m not the only one with this kind of problem, and it appears to be a long-standing issue, so it’s a candidate for improvement in future versions of Grav.
Wanted to develop a strategy for this too. @pmoreno, how about this? Using the example of adding JS assets to a Modular Page (instead of a module). Think you’ll get the point and can modify it if needed. (Tested on the Quark theme)
modular.html.twig
{% set hasJsAssets = ['modular/text', 'modular/features'] %}
{% block javascripts %}
...
{% for module in page.collection()|filter(module => module.template in hasJsAssets) %}
{% do assets.addJs('theme://js/' ~ module.template|split('/')|last ~ '.js', { group:'bottom' } ) %}
{% endfor %}
{{ parent() }}
{% endblock %}
Your suggestion is very interesting and has given me something to think about.
It only works if every modular page has exactly one JS file and the file name matches the template name (e.g. text.js, features.js).
If a module needs multiple JS files, or if the file names don’t match the template name, the system becomes too restrictive.
A more flexible and Grav-friendly solution is to let each modular page declare the JS assets it needs in its frontmatter.
1. Add JS files in the frontmatter of each modular page
In the modular page Markdown file (e.g. 01.features.md):