Inheritance in Headless Magnolia
Feb 22, 2022
--
Component Inheritance 1200x628

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

Java
  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

Java
  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:

inheritance1

You can now call the pages endpoint to query the “header” area from the home page:

This is what the response should look like:

Java
  {
 "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.

Java
  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:

Java
  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.

inheritance2

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:

About the author

Bartosz Staryga

Front-End Solution Architect, Magnolia

Bartosz is an expert in headless content management and front-end development at Magnolia. He designs and develops new Magnolia features and supports customers with their headless implementations from content types to APIs to integrations. Bartosz enjoys building new things and seeing them in action. He is also a trainer for Magnolia’s Headless training.