we’re working with Modular Pages in Grav and have a common use case where we’d like to reuse a complete modular section (e.g. a CTA block or feature overview) across multiple pages – with the following requirements:
The module should be maintained in one central place
It includes its own layout, media, and custom blueprint fields
On other pages, it should be referenced and rendered identically
No duplication or manual updates on each page
Ideally, editors should be able to insert such modules via the admin panel
We tried page.find() and the page-inject plugin, but these only help with content injection, not full modular rendering.
Has anyone found a clean way to do this?
Is there maybe a plugin or best practice to embed modular content (not just markdown) across pages?
Would we need a custom plugin that handles template rendering + context switching?
@christiana83, you can try the following: place all your content and configurations in the frontmatter (headers), simple example below:
{% set theme_config = config.theme %}
{% set cta = page.find(theme_config.cta_route).header.cta %}
{# or with fallback to section in theme configuration #}
{% set cta = page.find(theme_config.cta_route).header.cta ?: theme_config.cta %}
I don’t think you can put layout data to frontmatter. I think you’d need some plugin maybe, but not sure how it would work.
Maybe you could try creating one module template with a single blueprint field, where you can choose another module (I think it’s page field, or something). Then you could add this first module wherever you want and it would render another module which you choose in the config. This way, you could reuse any module with this single template
@b.da & @Karmalakas Thanks for your input! The idea of referencing another module via a page field and rendering it through a shared template is exactly the kind of workaround I had in mind.
I’ve been hesitant so far because I see a few potential challenges:
Modular subpages (like /shared/_cta1) typically only render correctly within their parent Modular Page context – so calling page.find() on them directly might not initialize the full layout, header fields, or media handling properly.
Including the referenced module’s template manually via Twig (include 'modular/xyz.html.twig' with { page: referenced }) could work, but might require passing in a lot of context manually – which could become hard to maintain.
I’m also concerned about what happens if the referenced module is changed later (e.g. new fields, updated layout): all referencing instances could break silently or render inconsistently.
That said: If you think this approach is stable enough and these concerns are manageable in practice, I’d be happy to give it a try and build a prototype. I just want to avoid introducing something that feels “clever” at first but becomes a maintenance issue later on.
Thanks again – I really appreciate the ideas and insights!
P.S. If I were to implement this as a plugin instead – do you have any thoughts on what could be handled more cleanly that way? I assume things like context injection, rendering isolation, and media management might be easier to control in a plugin environment?
umm, im not sure if i understand well so i need to confirm first.
you have many pages,
you want one or more part for those pages can be edited at once with no duplicate work,
you have the html-css-js code of that part or parts but just need to change its code like text, css rules, js rules, images (or can be whole part code but would be hard) etc.
you want to be able to show or hide that part or parts in whatever page you want.
if what i guess is right, you can do that with flex actually.
maybe because i dont use modular and use flex a lot, that is what i think.
I’ve also thought about using Flex Objects for this – especially since we already use them for other cases like author data. Flex definitely offers more flexibility in structuring and reusing data.
But in this case, the team’s goal is slightly different:
They want to have a kind of “module pool” – a collection of predefined modular components (with their own layout, text, images, etc.) that can be centrally maintained and then embedded into any modular page at a specific position, just like a regular modular section.
So it’s not just about showing/hiding or sharing data – it’s really about injecting a complete module block (layout + content + media) at a specific spot in a page, ideally without having to duplicate or reimplement anything.
That’s why we were exploring options like referencing other modular sections – even if it’s a bit hacky right now
Thanks for your input though – if you have any experience using Flex for full layout embedding, I’d be curious to hear more!
i have many different buttons, their html, css and js is whole different but they are all buttons.
i have them in separate twig files.
i already have a place in mind to put them which is under or top of elements with a common class which is for example “specla”.
if i want to use a button, i include it and use js to put where i want like top of 4. specla classed item.
at my case my page changes a lot so i need to use js to put buttons where i want
but if your pages have little difference which is most probably like you have 5-10 sections in your template, you can just put include to between all of them and just choose which to active from flex.
@Karmalakas Wow, that looks really promising – thanks for sharing!
Just to clarify:
Would it also support embedding the full module including its original template, like background colors, buttons, custom fields (e.g. button text, colors, etc.) – basically the entire design and structure as defined in the original module?
If that works, it would be absolutely GRANDIOS – exactly what we’ve been looking for!
@christiana83, please try testing it and feel free to create any critical issues you find
Just keep in mind, that you need to disable Twig cache on these new modules (as shown in video) for changes to appear instantly after editing the original module
@Karmalakas Thank you so much for offering the plugin for testing – I really appreciate it!
I’d love to try it out, but I’ll need a few days as I currently have some other issues to resolve first.
As soon as I find a quiet moment, I’ll definitely take a look and give you feedback. Thanks again for your support – really looking forward to testing it!
@Karmalakas Thank you for creating the Module Reuse plugin—it’s exactly the tool I was looking for. I installed it right away and began testing, but discovered that in order for it to respect the “Start in” setting and surface the correct module pages, we had to make two essential adjustments:
Make getRoot() dynamic
In user/plugins/module-reuse/classes/Config.php, replace the hard-coded "/modular" with a call to the Grav config so it always reads the admin’s “Start in” value:
<?php
namespace Grav\Plugin\ModuleReuse;
use Grav\Common\Grav;
class Config
{
public static function getRoot(): string
{
/** @var Grav $grav */
$grav = Grav::instance();
return (string) $grav['config']->get('plugins.module-reuse.root');
}
}
Allow full-page selection for the root setting
In your user/config/plugins/module-reuse.yaml, set show_all: true on the root field so editors can pick any page (e.g. our “Module Lib”) as the starting point:
root:
type: pages
label: Start in
show_all: true # Display all pages in the selection tree
show_root: false # Prevent choosing the root page itself
show_modular: false # Continue hiding other modular pages
description: Root page to start the tree for module selection
Would you prefer that I fork the repo and submit these changes as a pull request, or are these inline code snippets and comments sufficient?
Many thanks—this has genuinely saved me a ton of work, and I really appreciate all your hard work and help!
I actually pulled the code yesterday, but I must have grabbed it just before your fixes went in. I’ll fetch the latest from main now and test again—thanks for the heads-up!
What kind of pages are missing if it’s false? When I tested, show_all: true also made modules visible, which didn’t make sense to me. Changing to false only had hidden the modules in my case, so I wonder now if it’s possible to only hide the modules
We have over 1000 pages, and I can’t see all of them if I turn this off. But that has nothing to do with hidden pages, so it doesn’t matter. show_modular: false hides the modules.