How React Routing works using a Headless CMS
Tags: react, javascript, cms, tutorial
Published: 2019-09-06 13:17:55

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](https://agilitycms.com/resources/guide/why-agility-cms-has-page-management-as-a-headless-cms) 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](https://help.agilitycms.com/hc/en-us/articles/360031121692).  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](https://account.agilitycms.com/sign-up).

### 1: Clone the repo:

```shell
git clone https://github.com/agility/agility-create-react-app
```

### 2: Install npm dependencies:

```shell
npm install
```

### 3: Start the site locally:

```shell
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):

```javascript
{
	"/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.

```javascript
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).

```javascript
//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.

```javascript
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 **<ContentZone>** 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:

```javascript
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:

```javascript
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 **<Switch>** and **<Route>** components are controlled by the react-router, and we have a **<PageRouter> **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!