Inheritance in Headless Magnolia
A common requirement of many Magnolia projects is for multiple pages of a website to have a consistent structure and look, for example, using the same header and footer. Magnolia’s server side rendering solves this through component inheritance, which means that a page can use components from its parent. Inheritance displays content consistently on multiple pages across the site. When you update content on one page, every page in the page hierarchy displays the same updated content, saving editors the time and effort to configure this manually.
While Magnolia’s SPA renderer and Delivery API do not support component inheritance yet, you can easily build this behaviour into your code. I will show you how to do this using React as an example but the steps are very similar in Angular and Vue.
Configuring the Delivery API to Provide Area Data
The Delivery API allows you to read any node from JCR. To enable inheritance, enable your endpoint to return area nodes by adding mgnl:area to the nodeTypes property.
File: /spa-lm/restEndpoints/delivery/pages.yaml
class: info.magnolia.rest.delivery.jcr.v2.JcrDeliveryEndpointDefinition
workspace: website
depth: 10
systemProperties:
- mgnl:template
nodeTypes:
- mgnl:page
- mgnl:area
Now consider a “SPA Home” page template with the areas “header” and “main”.
File: /spa-lm/templates/pages/Home.yaml
title: SPA Home
dialog: spa-lm:pages/Home
templateScript: /spa-lm/webresources/build/index.html
renderType: spa
class: info.magnolia.rendering.spa.renderer.SpaRenderableDefinition
areas:
header:
title: Header area
availableComponents:
text:
id: spa-lm:components/Text
main:
title: Main area
availableComponents:
text:
id: spa-lm:components/Text
list:
id: spa-lm:components/List
This is what the page looks like:
You can now call the pages endpoint to query the “header” area from the home page:
This is what the response should look like:
{
"0": {
"@name": "0",
"@path": "/spa-home/header/0",
"@id": "ae5b767d-a2fb-44fd-8f65-f4c9577ac6eb",
"@nodeType": "mgnl:component",
"text": "Praesent comodo cursus magna, vet scelerisque nist consectetur et.",
"mgnt:template": "spa—tm:components/Text",
"@nodes": []
},
"@name": "header",
"@path": "/spa-home/header",
"@id": "ae5b767d-a2fb-4ff9-a8dc-4964-97ee-e9cc25c",
"@nodeType": "mgnl:area",
"00": {
"@name": "00",
"@path": "/spa-home/header/00",
"@id": "768110d6-93e0-466d-98b6-3acac4af4907",
"@nodeType": "mgnl:component",
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"mgnt:template": "spa—tm:components/Text",
"@nodes": []
},
"@nodes": [
"0", "00"
]
}
Headless Magnolia: The Delivery Endpoint API
The Headless Magnolia series explains features that allow you to use Magnolia as a headless CMS. Read our blog post for an introduction to the Delivery endpoint API.
Extending the Page Template
Now, extend the “Home page” template by a function that renders the header’s area only.
import React, { useState, useEffect } from 'react';
import { EditableArea } from '@magnolia/react-editor';
export function HomeHeader() {
const [header, setHeader] = useState();
useEffect(() => {
async function getHeader() {
const res = await fetch('/magnoliaAuthor/.rest/delivery/pages/spa-home/header');
const json = await res.json();
setHeader(json);
}
getHeader();
}, []);
return header ? <EditableArea content={header} /> : null;
}
function Home(props) {
const { title, main, header } = props;
return (
<>
{header && <EditableArea content={header} />}
<h1>{title}</h1>
{main && <EditableArea content={main} />}
</>
);
}
export default Home;
The HomeHeader method fetches the header area of the home page and renders it.
Even though we do not use HomeHeader in the “Home page” template (the home page will contain header data and this area can be used directly via props) keeping it close gives clarity and makes it easier to follow the code.
Configuring the Child Page Template
To “inherit” the header area from the home page, the child page needs to make use of the HomeHeader component:
import React from 'react';
import { EditableArea } from '@magnolia/react-editor';
import { HomeHeader } from './Home';
function Contact(props) {
const { title, main } = props;
return (
<>
<HomeHeader />
<h1>{title}</h1>
{main && <EditableArea content={main} />}
</>
);
}
export default Contact;
When the child page renders the HomeHeader component, it fetches the header data from the parent page. The Magnolia SPA Editor renders the component but does not allow for it to be edited on the child page. To edit the component, it has to be edited on the parent page.
Inheritance Without Inheritance
This simple example shows that we can easily bring inheritance as we know it from FreeMarker rendering into the world of SPA development. It requires a bit of extra code but the results and authoring experience are worth the extra effort.
You can find the entire example including the Magnolia Light Module and SPA code in my repository.
For more information and examples of using Magnolia’s Visual SPA Editor, please check our documentation for your front-end framework:
Learn more about Magnolia’s headless features
This Headless Magnolia series will cover the following topics:
Nodes Endpoint API and Commands Endpoint API
Inheritance in Headless Magnolia (you are here)