How React Routing works using a Headless CMS
Joel Varty
Posted on September 6, 2019
Agility CMS is unique in that it offers a REST API for Page Routing. It doesn't actually do any of the routing for you - it just provides you with a routing table object that represents the sitemap, and Page objects that represent each page.
This enables you, as a developer, to offload the setup of complex sitemaps to the content team. This can be a huge time-saver and makes developing websites using a CMS much faster.
In the guide Why Agility CMS has Page Management Capabilities I outline how pages are managed in the CMS using Page Templates and Modules.
Let's look at how that actually works in our React site.
Start with the code
First, get started with the Agility CMS React app. It's as easy as cloning a GitHub repo and running a command line to get started. Oh, you should also sign-up for a free Agility CMS account.
1: Clone the repo:
git clone https://github.com/agility/agility-create-react-app
2: Install npm dependencies:
npm install
3: Start the site locally:
npm start
How does the routing work?
It all starts with a sitemap object, which Agility CMS gives us via the REST API.
It looks something like this (I've simplified it a bit for clarity):
{
"/home": {
"title": "Home",
"name": "home",
"pageID": 2
},
"/posts": {
"title": "Posts",
"name": "posts",
"pageID": 3
}
}
Each route in the site is accessible from this object, so you can quickly lookup whether a route exists or not.
Here's how we use this in React.
async routePage() {
const api = this.props.agility.client;
try {
//get the sitemap route table
const sitemap = await this.getSitemap(api);
//get the path from the browser
const path = document.location.pathname.toLowerCase();
const pageInSitemap = sitemap[path];
//if we are on the homepage, get the first route
if (path === '/') {
const firstPagePathInSitemap = Object.keys(sitemap)[0];
pageInSitemap = sitemap[firstPagePathInSitemap];
}
//only proceed if this path is in the table
if (pageInSitemap) {
//get the actual page object
const page = await api.getPage({
pageID: pageInSitemap.pageID,
languageCode: this.props.agility.config.languageCode
});
//set this page in our state object
this.setPage(page, pageInSitemap);
} else {
//Could not find page
this.pageNotFound();
}
} catch (error) {
//Throw error
this.handleError('error getting sitemap :(', error);
}
}
We first load the sitemap, then we use the current location.pathname to check if the current page is an available route. If our current route is "/", then we use the first page in the sitemap.
Now, we take that route object, called pageInSitemap, and call getPage() with the pageID.
Once we have the page object, Aglity CMS gives us all the data we'll need to actually render this page.
In our file called agility.config.js, we setup which React Components will render for each Page Template and Module Definition that is configured on any available page object (this is all setup in the CMS earlier).
//Our Agility Modules
import RichTextArea from './modules/RichTextArea'
import Jumbotron from './modules/Jumbotron'
//Our Agility PageTemplates
import OneColumnTemplate from './pageTemplates/OneColumnTemplate'
export default {
guid: '...', //Set your guid here
fetchAPIKey: '...', //Set your fetch apikey here
previewAPIKey: '...', //set your preview apikey
languageCode: 'en-us',
channelName: 'website',
isPreview: true,
moduleComponents: {
RichTextArea,
Jumbotron
},
pageTemplateComponents: {
OneColumnTemplate
}
}
We have 2 page templates setup, as well as 4 different modules. Each will be rendered by a different component, which we specify with the import statements at the top, and each component will be delivered a props variable that has all the data it needs from Agility CMS.
Page Template Components
Let's take a look at the OneColumnTemplate component that renders our Page Template.
import React, { Component } from 'react';
import { ContentZone } from '../agility-react'
class OneColumnTemplate extends Component {
render() {
return (
<div className="one-column-template">
<ContentZone name='MainContentZone' {...this.props} />
</div>
);
}
}
export default OneColumnTemplate;
It's pretty simple - all we are doing here is dropping a component with a name attribute that matches the zone defined in our Page Template. We also pass through the props - we'll see how that's important when we render our Module components. The ContentZone component will now look at the Page object to render out the modules that have been dropped onto it by our content editors.
Module Components
Each Module in Agility CMS can have it's own properties. Those get passed into our components as props. Here's our JumboTron component, which is a simple example of rendering a heading and a sub-heading:
import React, { Component } from 'react';
import './Jumbotron.css'
class Jumbotron extends Component {
render() {
return (
<section className="jumbotron">
<h1>{this.props.item.fields.title}</h1>
<h2>{this.props.item.fields.subTitle}</h2>
</section>
);
}
}
export default Jumbotron;
In this case, it's a simple matter of outputting the title and subTitle properties into header tags. These have been setup in Agility CMS as Module Properties. As you add more properties to your modules with different content types, they become available to you as props.
One more thing...
There is a really important piece that I haven't spoken about yet: the react-router component, which allows us to only change a main section of each page when the route changes.
Take a look at our App.js file:
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom'
import './App.css';
//The Agility Router
import { PageRouter } from './agility-react'
//Shared Components
import GlobalHeader from './GlobalHeader'
class App extends Component {
componentDidMount() {
}
render() {
return (
<div className="App">
<GlobalHeader agility={this.props.agility} />
<main className="main">
<Switch>
<Route path="*" render={() => <PageRouter agility={this.props.agility} />} />
</Switch>
</main>
</div>
);
}
}
export default App;
The and components are controlled by the react-router, and we have a component that triggers all the logic that I described above.
That's a really simple look at how we perform routing in a React app with Agility CMS Pages.
I think it's kind of magical when you get all this in place and allow your content editors to create all kinds of pages and put modules on them wherever they please. It takes your work as a developer and multiplies it.
That's one of the ways that Agility CMS is the fastest CMS!
Posted on September 6, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.