Dynamic Routing & Filtering

Site Sections

On this site, you can see from the navbar and the “home page”[1] that I split my articles into four collections, based on their primary tag: games, charts, writing, and everything else. Each collection has its own URL location named as appropriate (‘everything else’ is the self-explanatory catch-all, and named other).

One of Ghost’s tutorials shows you how to create collections of posts by tag via the use of the filter property. It works similarly to the actual, real-life definition of a filter: you start with all your posts, and as it passes through each collection rule, those posts are taken away from what remains. Of course, you need to finish it off with a catch-all; like what I’ve done for mine with /other, you can put it behind whatever URL you like, or just leave it at the root if you had no plans to use that for a landing page.

This is why they state further down that order matters, i.e. it doesn’t make sense for the catch-all to come first.

There was also a mention inside the developer documentation for routing[2] that the primary tags that you decide upon should all be exclusively unique. So for me, it’s either a post about games, or charts, or writing. Technically speaking though, you could say charts is a subset of games, but this sort of exceptional distinction for your site is something you’ll have already established prior.

The Problem

So, you’ve started with every post and filtered them to your needs. Great – the catch-all should pick up the rest and you won’t need to specify any filters, right?

  /: home

    permalink: /games/{slug}/
    template: tag
    filter: primary_tag:games
  /charts/: # etc
  /writing/: # etc
    permalink: /other/{slug}/
    template: index
my routes.yaml, truncated for brevity

Turns out that’s not how it works. When I took a look at /other, I was getting less posts than expected on the page – if there were supposed to be 12 posts to a page, I would find seven on the first page, and then have more on the second page.

Stumped at first why this would be the case, I thought it had something to do with the theme’s logic using its own overridden rules defined somewhere else. Or maybe I mixed up some tags in one of the posts and it’s confused itself.

After a lot of searching around, I did a lap around the internet and found myself back at the developer docs again, this time landing upon one of the FAQ articles regarding problems with filtering:

In the current beta version of Dynamic Routing it’s necessary to specify the inverse of filters using - to ensure pagination is correct
In other words, my /other needed to have a primary tag filter excluding the previous tags, i.e. filter: primary_tag:-[games,charts,writing]. Otherwise, the posts that belong to these tags still "counted", but their contents were null. As in, no toilet roll.
Before I wrote this, the tutorial didn’t have the tag negation filter in its example code, which was what caused me to look for answers. It seems like they’ve updated it since, although without an explicit explanation (however, one can logically deduce why).

[1] The quotation marks around home page are there because, by default without any customisation, a Ghost site’s home page at its root will show you the most recent posts. I overrode this by instead pointing the root to the logo-and-four-tile custom landing page.

[2] I’d like to point out (a) this is inside the theming documentation, which the typical end user likely wouldn’t check, and (b) as of writing, the More info here link doesn’t actually link to anywhere.