If you've ever tried to display only posts with a specific tag on your Ghost blog's homepage, you might have started by hacking away at your Handlebars templates. I recently wanted to show only my development-related posts on my homepage, and after a bit of trial and error, I found a much cleaner solution than I initially thought possible.
The wrong way to do it
My first instinct was to filter posts directly in the homepage template (home.hbs) like this:
{{#get "posts" limit="8" filter="primary_tag:'development'"}}
{{#foreach posts visibility="all"}}
// ... (posts)
{{/foreach}}
{{/get}}
This approach seemed sensible at first glance – just grab the posts with the development tag and display those.
But there's a problem. If you try this method, you'll quickly discover that your pagination breaks. You'll end up with a mismatch between the number of posts being displayed and the number of pages in your pagination controls. This happens because the pagination is still counting all your posts, not just the filtered ones. Not ideal!
The proper solution
After poking around in the Ghost admin panel, I discovered a much more elegant solution. Ghost has a feature called Dynamic Routing that lets you customize your site's URL structure – and it also allows you to set up filtered content streams.
You can find this feature under Settings > Labs
in your Ghost admin panel.
Dynamic Routing works through a configuration file called routes.yaml. By default, it looks something like this:
routes:
collections:
/:
permalink: /{slug}/
template: index
taxonomies:
tag: /tag/{slug}/
author: /author/{slug}/
Setting up a filtered homepage
To filter your homepage content by tag, all you need to do is add a routes section to the YAML file with the following configuration:
routes:
/:
controller: channel
filter: 'tag:development'
This sets up what Ghost calls a "channel" for your homepage route (/
), and filters all the content by the development tag. If you wanted to filter by a different tag, just replace 'development' with the slug of your desired tag.
The beauty of this approach is that it handles pagination correctly. Since the filtering happens at the routing level, Ghost knows exactly how many posts match your filter and can create the proper number of pages.
Why this is better
Using Dynamic Routing rather than template-level filtering gives you several advantages:
- Pagination works correctly out of the box
- You don't have to modify your theme files
- It's cleaner and more maintainable
- The URLs remain simple and user-friendly
This approach is much more aligned with how Ghost is designed to work. Rather than fighting against the framework, we're using its built-in capabilities to achieve what we want.
If you're looking to create more complex filtering or multiple filtered sections on your site, Dynamic Routing can handle that too. Check out the Ghost documentation for more advanced use cases.
I hope you've found this helpful! If you spot any errors or have alternative approaches to filtering content in Ghost, please leave a comment below.