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.
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:
/ : 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:
/ : 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:
# ./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:
$ git clone https://github.com/styxlab/gatsby-starter-try-ghost.git jamify-routing
and change into the work directory:
$ cd jamify-routing
Import all dependencies with
[jamify-routing]$ yarn
and check that you can build...
[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:
- Change
routes.yaml
in your headless Ghost CMS. - Use a reverse proxy in front of your site and configure the proxy to perform the routing from
/
to/blog
. - 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:
// 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
:
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...
...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 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:
[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:
// 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:
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
:
//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
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 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:
{
"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 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:
/my-best-article
you could make them available under the URI:
/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:
Either change the file directly or upload a modified version in the admin panel:
# ./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):
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:
// 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
:
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/
.
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!
This post was originally published at jamify.org on April 28, 2020.
Top comments (0)