Routing with Jamify
Tags: webdev, gatsby, ghost, cms
Published: 2020-07-12 14:43:55

---
title: Routing with Jamify
published: true
date: 2020-04-28 07:00:00 UTC
tags: webdev, gatsbyjs, ghost, cms
canonical_url: https://www.jamify.org/2020/04/28/routing-with-jamify/
---

This tutorial is a primer on dynamic routing with Jamify. Learn the basic concepts of routing and URL pattern matching and apply your knowledge to common use cases: Add a landing page to your blog, look into the structure of permalinks and learn how to create multi-language sites with collections.

 ![Routing with Jamify](https://images.unsplash.com/photo-1510511233900-1982d92bd835?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ)

The basic purpose of a router is to match URL patterns and perform actions as a result, such as showing you a page or submitting a form request. Routing is at the heart of your web application and drives the user interface.

It comes pre-configured and chances are you do not need to change anything. However, if you need to customize your site layout, a good understanding of your site's routing system, and the URL patterns it uses, is of great advantage.

URL patterns are indexed by search engines and it therefore comes at no surprise that routing is an integral part of search engine optimization. This is another reason why you may take an interest in this topic.

## Site layout

Before looking into how to customize the routing system, let's first look at the standard site layout as it is pre-configured. The Ghost CMS comes with an extremely simple site structure, centred around `posts`.

A post is amended by `authors` and `tags`, which can be viewed as properties of of `posts`. Finally, there are `pages` which are almost identical to `posts`. With this data model in mind, the standard site structure is quite easy to understand:

```text
/ : index of all articles

/post1/ : first article
/post2/ : second article
.
.

/tag/tag1/ : all articles tagged with tag1
/tag/tag2/ : all articles tagged with tag2
.
.

/author/author1/ : all articles with author1
/author/author2/ : all articles with author2
.
.
```

In reality, this simple picture is a bit more involved due to pagination:

```text
/ : index of articles, page 1
/page/2/ : index of articles, page 2
/page/3/ : index of articles, page 3

/post1/ : first article
/post2/ : second article
.
.

/tag/tag1/ : articles tagged with tag1, page 1
/tag/tag1/2/ : articles tagged with tag1, page 2
/tag/tag1/3/ : articles tagged with tag1, page 3
.
/tag/tag2/ : articles tagged with tag2, page 1
/tag/tag2/2/ : articles tagged with tag2, page 2
.
.

/author/author1/ : articles with author1, page 1
/author/author1/2/ : articles with author1, page 2
/author/author1/3/ : articles with author1, page 3
.
/author/author2/ : articles with author2, page 1
/author/author2/2/ : articles with author2, page 2
.
.
```

> Although Jamify's Casper theme uses infinite scroll, paginated pages are still generated, so they can be indexed by search engines!

## Instructing the Router

With a growing number of posts, authors and tags, you can imagine how the above list will grow. It would be very inefficient to lay out every single route to the router.

That's where patterns come to the rescue and it's these patterns that you need to instruct the router with. Here is a first glimpse into such an instruction file:

```yaml
# ./content/settings/routes.yaml

routes:

collections:
  /:
    permalink: /{slug}/
    template: index

taxonomies:
  tag: /tag/{slug}/
  author: /author/{slug}/
```

I'm sure you can recognize the patterns for `tags` and `authors` as well as the ones for `posts` under the `permalink` key. I will discuss this file in more depth later, for now simply remember that `routes.yaml` is one place where the router can be configured.

## Add a landing page

Many people run a blog as part of a larger site. In this case, you typically have a landing page that is different from the blog index page.

How do you integrate a landing page at the root URL, given that your Jamify site already shows an index page under the same address? Routes must be unique, obviously the Jamify index page should be moved to some other URL.

But wait..., if your blog index page is moved, for example, from `https://site.com` to `https://site.com/blog`, what about all other routes? Wouldn't it make sense to also move the posts, e.g. from `https://site.com/post1` to `https://site.com/blog/post1`?

Although not required, it is highly recommended to leave the original site structure intact and move all routes one level down, so the root path for the Jamify blog content changes from `/` to `/blog`. This also prevents naming conflicts with other pages that you might add to your new root later.

### Start a new project

Let's start with a new Jamify project and go through the steps required to add a landing page. First, clone the Jamify starter as usual:

```bash
$ git clone https://github.com/styxlab/gatsby-starter-try-ghost.git jamify-routing
```

and change into the work directory:

```bash
$ cd jamify-routing
```

Import all dependencies with

```bash
[jamify-routing]$ yarn
```

and check that you can build...

```bash
[jamify-routing]$ yarn develop
```

...and access the Jamify demo page at `http://localhost:8000`. This address is your route domain and I refer to it with the foreslash `/` from here on. Play a little with this demo and take note of the URL structure when navigating through posts and pages.

### Move all routes

To make room for the landing page, we want to move all existing routes one level down from `/` to `/blog`. There are multiple ways to achieve this:

1. Change `routes.yaml` in your headless Ghost CMS.
2. Use a reverse proxy in front of your site and configure the proxy to perform the routing from `/` to `/blog`.
3. Use the `basePath` configuration option that Jamify provides.

For this particular use case, the `basePath` option is by far the easiest. Open `routesConfig.js` in your favourite editor and add the `basePath` option as follows:

```javascript
// routesConfig.js
module.exports = {

    basePath: `/blog`,
    
    collections: [],
}

```

> You can also move your site to a different path, for example to `/publications` or down to a deeper level, e.g. `/pubs/blog`.

Rebuild your project with `yarn clean; yarn develop` and open it in development mode, again at `http://localhost:8000`:

![Routing with Jamify](https://cms.jamify.org/content/images/2020/04/jamify-basepath-404-1.png)

Oops, where did your page go? Indeed, after moving your site the router cannot find anything at the root index `/` anymore!

> The Gatsby 404 development page shows a number of URLs that it can find. Note that all routes are moved under `/blog` now.

Go to `http://localhost:8000/blog` to see your blog index page again...

![Routing with Jamify](https://cms.jamify.org/content/images/2020/04/jamify-basebath-blog.png)

...and navigate through the pages to see if everything is working as expected.

### Install the landing page

I'm going to install a nice Gatsby landing page developed by [Kyle Gill](https://github.com/gillkyle/gatsby-starter-landing-page) with some small modifications. I had to localize the global css styles of the landing page, so they don't interfere with the Jamify site styles.

As the landing page is not available as a Gatsby theme, we install it as a _local_ plugin. Make sure that you are in the work directory and clone the landing page into the plugin folder:

```bash
[jamify-routing]$ git clone https://github.com/styxlab/gatsby-starter-landing-page.git plugins/landing-page
```

You can then add the local plugin in `gatsby-config.js` as the first plugin in the array:

```javascript
// gatsby-config.js
plugins: [
    {
        resolve: `./plugins/landing-page`,
    },
]
```

Rebuild with `yarn develop` and view your site in the browser at `http://localhost:8000`. You should be greeted with the newly added landing page:

![Routing with Jamify](https://cms.jamify.org/content/images/2020/04/jamify-landing-page.png)

> You can click on the `Get Early Access` button to navigate to your blog!

### Finding back home

This looks all pretty cool. You moved all your blog pages under `/blog` and made space for the landing page that is now installed as your root page. Further more, you can reach you blog from the landing page.

There is just a tiny flaw. Once you have navigated to your blog, you cannot go back to you landing page other than using the back button of your browser. When you click `Home` in the navigation bar, you'll stay on your blog index page.

This may be exactly what you want, especially if you rename the menu item form `Home` to `Blog`. However, it should be possible to provide a navigation item that brings you back to the landing page.

There is a new option available in Jamify that was designed exactly for that purpose: overriding menu links. Simply add this one-liner to your `siteConfig.js`:

```javascript
//siteConfig.js
module.exports = {

    // leave all previous options untouched and add:
    overwriteGhostNavigation: [{ label: `Home`, url: `/` }],
}

```

The option `overwriteGhostNavigation` looks up existing navigation items by label and replaces the existing `url` with the one given in the configuration. That way we can override the `Home` link from `/blog` to `/`.

Save the file, rebuild with `yarn develop` and check that the `Home` navigation item show `localhost:8000` instead of `localhost:8000/blog`

![Routing with Jamify](https://cms.jamify.org/content/images/2020/04/jamify-landing-home-1.png)

and brings you back home to the landing page.

## Source your own content

In the next chapter you are going to make changes in the headless Ghost CMS. As you only have read access to the demo site, it's now time to source your own site content. Read the [Getting started with Jamify](https://cms.jamify.org/2020/05/19/getting-started-with-jamify/) tutorial, if you are new to sourcing in your own content.

In a nutshell, you need to create a new `.ghost.json` file in the work directory and copy your Content API keys in:

```json
{
  "development": {
    "apiUrl": "https://cms-jamify.gotsby.org",
    "contentApiKey": "e5e630bdbbf0a09dd0f7980c84"
  },
  "production": {
    "apiUrl": "https://cms-jamify.gotsby.org",
    "contentApiKey": "e5e630bdbbf0a09dd0f7980c84"
  }
}
```

Rebuild your site with `yarn clean; yarn develop` and you should see your new content at `https://localhost:8000` (the starter page is still the same!)

## Designing permalinks

[_Cool URIs don't change_](https://www.w3.org/Provider/Style/URI) is an entertaining article about why you should try to make your URIs permanent. An important step towards this goal is a naming convention that outlives any changes on your site.

A popular idea is to choose a date schema, where your article links are structured according to date. This is also common practice in archiving, and what couldn't be more permanent than an archive?

Instead of providing posts under the URI:

```text
/my-best-article
```

you could make them available under the URI:

```text
/2020/05/01/my-best-article
```

effectively following a `/{year}/{month}/{day}/{article-name}` schema.

### Permalinks in routes.yaml

This schema can be enforced by making modifications to the `permalink` property in `routes.yaml`.

> It's important to understand that `routes.yaml` is a file on your headless Ghost CMS and has no direct counterpart in your Gatsby configuration.

You can also download, modify and upload this file from the Ghost Admin interface:

![Routing with Jamify](https://cms.jamify.org/content/images/2020/04/jamify-routes-yaml.png)

Either change the file directly or upload a modified version in the admin panel:

```yaml
# ./content/settings/routes.yaml

routes:

collections:
  /:
    permalink: /{year}/{month}/{day}/{slug}/
    template: index

taxonomies:
  tag: /tag/{slug}/
  author: /author/{slug}/
```

Rebuild your site with `yarn clean; yarn develop` and look how your article links changed to permalinks (remember that the `basePath` option is still active):

![Routing with Jamify](https://cms.jamify.org/content/images/2020/04/jamify-permalinks.png)

> This is magic: how does the static site builder know how you configured the router of your headless CMS? Easy: the full URL path of every site can be used to create every page with exactly the same path, effectively replicating all CMS routes!

### Dynamic Variables

The permalinks in the shown configuration contain the dynamic variables `{year}`, `{month}` and `{day}`. Here is a list of all variables that you can use in your permalinks:

- `{id}` - unique identifier, e.g. `67982e808bcf48110190efd67`
- `{slug}` - the post slug, e.g. `my-article`
- `{year}` - publication year, e.g. `2020`
- `{month}` - publication month, e.g. `05`
- `{day}` - publication day, e.g. `28`
- `{primary_tag}` - slug of first tag listed in the post, e.g. `news`
- `{primary_author}` - slug of first author, e.g. `martin`

If you think about which of these are _permanent_ with respect to your post, the publication date, the slug and the primary author should outlive all changes to your website. If one of those change, it can be considered a different article.

The `{id}` could change, if you migrate your articles to a new blogging system, a `news` tag could be switched to an `archived` tag at later time. That's why the use of `{id}` and `{primary_tag}` is discouraged in permalinks.

## Creating Collections

Collections are a great way to create distinct areas of non-overlapping content. A typical use case is different routes in a multi-language site that are segmented according to locales.

To configure collections, there is a new `collections` configuration available that you can add to the `routesConfig.js` file:

```javascript
// routesConfig.js
module.exports = {

    basePath: `/blog`,
    
    collections: [{
        path: `speeches`,
        selector: node => node.primary_tag && node.primary_tag.slug === `speeches`,
    }],
}

```

As `collections` is an array, you can configure as many collections as you like. Each collection consists of a `path` and a `selector`. The path is added to the `basePath` and the `selector` is a Javascript function that operates on `posts`. JS functions are extremely flexible, so you can create collections based on `tags`, `authors` or on any other field that its available in `posts`!

The above configuration creates two collections, namely `/speeches/` and everything else under `/`. Thus, you now have two index pages.

> Note that Jamify collections ensure non-overlapping content, even if your selector functions overlap. This is done by applying selector functions only on the remainders of the previous selector results.

In order to be able to access the new `/speeches/` collection, add a new navigation item under the _Design_ and call it `Speaker's Corner`:

![Routing with Jamify](https://cms.jamify.org/content/images/2020/04/jamify-collections-design.png)

Save the new design and rebuild your site. When you inspect your site, you can see that all articles with primary tag `speeches` have different routes. They are all prefixed with `/speeches/`.

 ![Routing with Jamify](https://cms.jamify.org/content/images/2020/05/jamify-collections-index.png)

 ![Routing with Jamify](https://cms.jamify.org/content/images/2020/05/jamify-collections-speeches.png)

 ![Routing with Jamify](https://cms.jamify.org/content/images/2020/05/jamify-collections-author-2.png)

Also note that articles that fall into the `speeches` collection do not show up in the root index, so no duplicate content!

Finally, observe that taxonomies such as `tags` and `authors` remain unaffected. So, all articles for one author are shown even if they fall in different collections. Likewise, all articles for one tag are shown across collections.

> If you are into details, have a look at the preview section at the bottom of each post. As you can see, previous and next posts are only shown for the ones that fall into the same collection. This is important if you use collections for multi-language sites, because you do not want references to posts from another locale.

## Summary

Designing a site structure that is fun to navigate, easy to maintain and where URIs persist over many years despite all the changes that your site may go through, is definitely challenging.

That's why it is important that you acquire a solid understanding of the tools that Jamify provides for dynamic routing. You have seen how these tools can be applied to popular use cases such as integrating a landing page, using permalinks and collections. You are now ready to apply the same principles to your own site!

> [Do you want early access to Blogody, the brand new blogging platform that I am creating? Just sign-up on the new Blogody landing page and be among the first to get notified!](https://www.blogody.com/landing)

***

This post was originally published at [jamify.org](https://www.jamify.org/2020/04/28/routing-with-jamify/) on April 28, 2020.