How to use Modular (pages, content) in Grav?

I have the following code as carousel in my HTML theme (a theme with no Grav in the backend):

<!-- Carousel Start -->
    <div class="container-fluid p-0 wow fadeIn" data-wow-delay="0.1s">
        <div id="header-carousel" class="carousel slide" data-bs-ride="carousel">
            <div class="carousel-inner">
                <div class="carousel-item active">
                    <img class="w-100" src="img/carousel-1.jpg" alt="Image">
                    <div class="carousel-caption">
                        <div class="container">
                            <div class="row justify-content-center">
                                <div class="col-lg-8">
                                    <h1 class="display-1 text-white mb-5 animated slideInDown">Solidarisch. Lokal. Gesund. Lecker.</h1>
                                    <a href="" class="btn btn-primary py-sm-3 px-sm-4">Explore More</a>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="carousel-item">
                    <img class="w-100" src="img/carousel-2.jpg" alt="Image">
                    <div class="carousel-caption">
                        <div class="container">
                            <div class="row justify-content-center">
                                <div class="col-lg-7">
                                    <h1 class="display-1 text-white mb-5 animated slideInDown">Was ist eine Solawi?</h1>
                                    <a href="" class="btn btn-primary py-sm-3 px-sm-4">Explore More</a>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <button class="carousel-control-prev" type="button" data-bs-target="#header-carousel"
                data-bs-slide="prev">
                <span class="carousel-control-prev-icon" aria-hidden="true"></span>
                <span class="visually-hidden">Previous</span>
            </button>
            <button class="carousel-control-next" type="button" data-bs-target="#header-carousel"
                data-bs-slide="next">
                <span class="carousel-control-next-icon" aria-hidden="true"></span>
                <span class="visually-hidden">Next</span>
            </button>
        </div>
    </div>
    <!-- Carousel End -->

I wanna do a modular with this code above.

So I has done the folloding code in “user/pages/modular/carousel.md”:

---
title: Carousel
content:
    items:
        - image:
              label: Image 1
              type: filepicker
              folder: '@self'
              preview_images: true
              accept:
                - image/*
          title:
              label: Title 1
              type: text
          caption:
              label: Caption 1
              type: textarea
        - image:
              label: Image 2
              type: filepicker
              folder: '@self'
              preview_images: true
              accept:
                - image/*
          title:
              label: Title 2
              type: text
          caption:
              label: Caption 2
              type: textarea
---

Then I craeted in “user/themes/mytheme/modular/carousel.html.twig”:

{% set content = page.content %}
{% set items = content.items %}
{% set id = 'header-carousel-' ~ random_string(8) %}
{% set delay = 0.1 %}

<div class="container-fluid p-0 wow fadeIn" data-wow-delay="{{ delay }}s">
    <div id="{{ id }}" class="carousel slide" data-bs-ride="carousel">
        <div class="carousel-inner">
            {% for item in items %}
            {% set active = (loop.index == 1) ? 'active' : '' %}
            <div class="carousel-item {{ active }}">
                <img class="w-100" src="{{ item.image.url }}" alt="{{ item.image.alt }}">
                <div class="carousel-caption">
                    <div class="container">
                        <div class="row justify-content-center">
                            <div class="col-lg-8">
                                <h1 class="display-1 text-white mb-5 animated slideInDown">{{ item.title }}</h1>
                                <p>{{ item.caption }}</p>
                                <a href="" class="btn btn-primary py-sm-3 px-sm-4">Explore More</a>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            {% endfor %}
        </div>
        <button class="carousel-control-prev" type="button" data-bs-target="#{{ id }}" data-bs-slide="prev">
            <span class="carousel-control-prev-icon" aria-hidden="true"></span>
            <span class="visually-hidden">Previous</span>
        </button>
        <button class="carousel-control-next" type="button" data-bs-target="#{{ id }}" data-bs-slide="next">
            <span class="carousel-control-next-icon" aria-hidden="true"></span>
            <span class="visually-hidden">Next</span>
        </button>
    </div>
</div>

In my “user/themes/mytheme/templates/home.html.twig” I put the following code into the template:


   {% block carousel %}
    <!-- Carousel Start -->
    {% include 'modular/carousel.html.twig' %}
    <!-- Carousel End -->
    {% endblock %}

In the Admin-Panel I can see he carousel Modular-Page, under the tree of pages.

How can I use the “carousel.md” made “form” to edit the content for the carousel? And how can I use it to see this carousel on my “Home”-Page in Grav? I can not see any form to fill out in the backend to create the carousel modular.

@wmcig, I’m afraid your post is a bit confusing to me…

May I suggest to download the One-Page Site skeleton and pay attention to:

  • The folder structure /user/pages/01.home/** containing the modular page and its child modules.
  • user/themes/quark/blueprints/**
    This is where the forms for the modular page and its child modules are being defined.
    • Have a look at the docs about Page Blueprints. This is how a form in Admin is being defined.
  • Have a look at /user/pages/01.home/modular.md which defines the modular page and the collection of modules to use.
    • Have a look at the docs about Collections of pages/modules
  • Have a look at /user/themes/quark/templates/modular.html.twig and observe how the content of modules are being included into the template of the modular.

I started to create my on Grav theme. I put the navigation into it and I tried to use the Modular Pages in my theme.

I created the modular.html.twig file with the following code:

{% for module in page.collection() %}
    {{ module.content|raw }}
{% endfor %}

In default.html.twig:

{% extends 'partials/base.html.twig' %}

{% block content %}
	<p>{{ page.content|raw }}</p>
{% endblock %}

In base.html.twig:

{% set theme_config = attribute(config.themes, config.system.pages.theme) %}
<!DOCTYPE html>
<html lang="{{ grav.language.getActive ?: grav.config.site.default_lang }}">

{% block head %}
        {% include 'partials/head.html.twig' %}
    {% endblock %}
<body>



    {% block topbar %}
        {% include 'partials/topbar.html.twig' %}
    {% endblock %}


    <!-- Navbar Start -->
       {% block nabber %}
        {% include 'partials/navbar.html.twig' %}
    {% endblock %}
    <!-- Navbar End -->

    {% block carousel %}
    <!-- Carousel Start -->

    <!-- Carousel End -->
{% endblock %}

<div class="container-xxl py-5">
        <div class="container">
<div class="row wow fadeInUp" data-wow-delay="0.3s">
<div class="col-12">

{% block content %}
	<p>{{ page.content|raw }}</p>
{% endblock %}
</div>
</div>
</div>
</div>



{% block footer %}
        {% include 'partials/footer.html.twig' %}
    {% endblock %}


    <!-- Back to Top -->
    <a href="#" class="btn btn-lg btn-primary btn-lg-square rounded-circle back-to-top"><i class="bi bi-arrow-up"></i></a>


{% block javascripts %}

    <!-- JavaScript Libraries -->
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js"></script>

    {% do assets.addJs('theme://lib/wow/wow.min.js') %}
    {% do assets.addJs('theme://lib/easing/easing.min.js') %}
    {% do assets.addJs('theme://lib/waypoints/waypoints.min.js') %}
    {% do assets.addJs('theme://lib/owlcarousel/owl.carousel.min.js') %}
    {% do assets.addJs('theme://lib/counterup/counterup.min.js') %}
    {% do assets.addJs('theme://lib/parallax/parallax.min.js') %}
    {% do assets.addJs('theme://lib/isotope/isotope.pkgd.min.js') %}
    {% do assets.addJs('theme://lib/lightbox/js/lightbox.min.js') %}

    <!-- Template Javascript -->
    {% do assets.addJs('theme://js/main.js') %}
{% endblock %}
{{assets.js('bottom')|raw}}
</body>

</html>

My Modular Page is called team.html.twig:

<div class="text-center mx-auto wow fadeInUp" data-wow-delay="0.1s" style="max-width: 500px;">
                <p class="fs-5 fw-bold text-primary">Our Team</p>
                <h1 class="display-5 mb-5">Dedicated & Experienced Team Members</h1>
            </div>
            <div class="row g-4">
{% for member in page.header.members %}
                <div class="col-lg-4 col-md-6 wow fadeInUp" data-wow-delay="0.1s">
                    <div class="team-item rounded">
                        <img class="img-fluid" src="{{page.media[member.image].url}}" alt="">
                        <div class="team-text">
                            <h4 class="mb-0">{{ member.name }}</h4>
                            <p class="text-primary">{{ member.info }}</p>
                            <div class="team-social d-flex">
                                <a class="btn btn-square rounded-circle me-2" href=""><i class="fab fa-facebook-f"></i></a>
                                <a class="btn btn-square rounded-circle me-2" href=""><i class="fab fa-twitter"></i></a>
                                <a class="btn btn-square rounded-circle me-2" href=""><i class="fab fa-instagram"></i></a>
                            </div>
                        </div>
                    </div>
                </div>
{% endfor %}
</div>

In user/pages/02.team. There is a modular.md file:

---
title: team
menu: Team
onpage_menu: false
body_classes: "modular header-image fullwidth"

content:
    items: '@self.modular'
    order:
        by: default
        dir: asc
        custom:
            - _aboutus
---

In user/pages/02.team/_aboutus, there are three image-files and team.md:

---
title: Team
members:
    -
        name: 'Doris Wagner'
        image: doris.jpg
        title: 'Landscape Desoigner'
        social_twitter: '#'
        social_facebook: '#'
        social_instagram: '#'
    -
        name: 'Jonny Ramirez'
        image: jonny.jpg
        title: 'Garden Designer'
        social_twitter: '#'
        social_facebook: '#'
        social_instagram: '#'
    -
        name: 'Diana Ashleeter'
        image: diana.jpg
        title: 'Senior Gardener'
        social_twitter: '#'
        social_facebook: '#'
        social_instagram: '#'
---

## Meet our team
## Dedicated & Experienced Team Members

In the Admin Panel I can see the team-Page (type modular). There I can see in the frontmatter this code:

title: team
menu: Team
onpage_menu: false
body_classes: "modular header-image fullwidth"

content:
    items: '@self.modular'
    order:
        by: default
        dir: asc
        custom:
            - _aboutus

In the modular child page _aboutus I can see in frontmatter:

title: Team
members:
    -
        name: 'Doris Wagner'
        image: doris.jpg
        title: 'Landscape Desoigner'
        social_twitter: '#'
        social_facebook: '#'
        social_instagram: '#'
    -
        name: 'Jonny Ramirez'
        image: jonny.jpg
        title: 'Garden Designer'
        social_twitter: '#'
        social_facebook: '#'
        social_instagram: '#'
    -
        name: 'Diana Ashleeter'
        image: diana.jpg
        title: 'Senior Gardener'
        social_twitter: '#'
        social_facebook: '#'
        social_instagram: '#'

If I click on my homepage, I can see the “Home”-Page (starting page). When I click in the menu on “Team”, I can only see the content of the “_aboutus” - Modular Page.

No navigation or footer. No CSS and no JS.

What is wrong? I wanna see the content within my theme styling, etc.

To make the carousel modular content editable in the Grav backend, you need to define a blueprint for the carousel.md file. The blueprint defines the form fields that will be used to edit the content.

Create a file called carousel.yaml in the user/themes/mytheme/blueprints/modular directory with the following content:

title: Carousel
@extends’:
type: default
context: blueprints://pages

form:
fields:
content:
type: tabs
active: 1

        fields:

            items:
                name: items
                type: list
                label: Carousel Items
                fields:
                    .image:
                        type: filepicker
                        label: Image
                        folder: '@self'
                        preview_images: true
                        accept:
                            - image/*
                    .title:
                        type: text
                        label: Title
                    .caption:
                        type: textarea
                        label: Caption

This blueprint defines the form fields for the carousel items, including the image, title, and caption.

Now, in the Grav backend, when you edit the carousel.md modular page, you should see the form fields for each carousel item.

To display the carousel on your home.html.twig template, you need to fetch the modular page and its content. Update the template code as follows:
{% extends ‘partials/base.html.twig’ %}

{% block content %}
{% for module in page.collection() %}
{% if module.template == ‘modular/carousel’ %}
{% include ‘modular/carousel.html.twig’ with { ‘content’: module.content } %}
{% endif %}
{% endfor %}
{% endblock %}
This code loops through all modular pages in the collection and checks if the template matches ‘modular/carousel’. If it does, it includes the carousel.html.twig template and passes the module content to it.

With these changes, you should be able to edit the carousel content in the Grav backend and see it displayed on your home page.