Ghost Theme Customization — Navigation, Custom Routes and Dynamic Routing
In this tutorial, you'll learn advanced Ghost theme customization — configuring navigation menus, creating custom routes with routes.yaml, setting up dynamic routing for custom collections, and extending your theme with custom templates.
What You'll Learn
- Configuring primary and secondary navigation in Ghost
- The routes.yaml file structure and syntax
- Creating custom routes for content collections
- Dynamic routing with channels and templates
- Regular expressions in custom routes
- Custom collections with filters and pagination
- Custom page templates and template inheritance
- Redirects and URL management with routes.yaml
- Best practices for navigation and URL structure
Why It Matters
By default, Ghost shows all posts in reverse chronological order. Custom routes let you create entirely different site structures — a featured section at a specific URL, a paginated tutorial collection, or a custom landing page with its own template. If you want your site to look and feel unique, custom routes are the key. They give you complete control over your site's URL structure and content organization.
Real-World Use
A magazine site needs three content sections: "News" (daily articles), "Features" (long-form pieces), and "Podcast" (audio episodes). Each section needs its own URL (/news/, /features/, /podcast/), its own template with different styling, and separate RSS feeds. The developer uses routes.yaml to define three custom channels, each filtered by a specific tag, with its own template and pagination settings.
Learning Path
flowchart LR A["Custom Themes"] --> B["Theme Customization
You are here"]:::current B --> C["Membership System"] C --> D["Content Gating"] classDef current fill:#38bdf8,color:#0f172a,stroke-width:2px
Navigation Configuration
Ghost has two navigation areas: primary and secondary.
Primary Navigation
Primary navigation appears in the main site header. Configure it in Settings > Navigation.
# Example primary navigation
- label: Home
url: /
- label: Blog
url: /blog/
- label: About
url: /about/
- label: Contact
url: /contact/
Secondary Navigation
Secondary navigation typically appears in the footer. Same configuration interface.
# Example secondary navigation
- label: Privacy Policy
url: /privacy/
- label: Terms
url: /terms/
- label: RSS
url: /rss/
Navigation in Templates
<!-- Primary navigation -->
<nav class="primary-nav">
{{navigation}}
</nav>
<!-- Secondary navigation -->
<nav class="secondary-nav">
{{navigation type="secondary"}}
</nav>
Navigation Helper Options
<!-- With custom class -->
<nav>{{navigation class="main-menu"}}</nav>
<!-- Current page highlighting -->
{{#foreach navigation}}
<a href="{{url}}" class="{{#if current}}active{{/if}}">{{label}}</a>
{{/foreach}}
The current property is true when the link's URL matches the current page.
The routes.yaml File
routes.yaml is a configuration file in content/settings/ that defines custom routing rules for your Ghost site.
Location
content/settings/routes.yaml
Basic Structure
routes:
/my-custom-url/:
template: custom-template
controller: channel
filter: tags:[my-tag]
limit: 10
data: page.about
collections:
/my-collection/:
permalink: /my-collection/{slug}/
template: my-collection
filter: tags:[my-tag]
limit: 10
taxonomies:
tag: /topic/{slug}/
author: /writer/{slug}/
Routes vs Collections vs Taxonomies
| Section | Purpose |
|---|---|
routes |
Single URL → single template |
collections |
URL + paginated content listing |
taxonomies |
Override default tag/author URL patterns |
Custom Routes
Routes map a single URL to a template.
routes:
/about/:
template: page-about
data: page.about
/contact/:
template: page-contact
data: page.contact
/featured/:
template: featured
controller: channel
filter: featured:true
limit: 10
Route Options
| Option | Description |
|---|---|
template |
The Handlebars template to use (without .hbs extension) |
controller |
channel for content listings, omit for static routes |
filter |
Ghost filter syntax to select content |
limit |
Items per page (for channel controllers) |
data |
Fetch a specific page/post by slug (e.g., page.about) |
order |
Sort order for content (e.g., published_at desc) |
Static Route Example
# /about/ renders page-about.hbs with content from the "about" page
routes:
/about/:
template: page-about
data: page.about
This lets you create a page in the admin called "About" with slug "about," and it renders using your custom page-about.hbs template at /about/.
Channel Route Example
# /featured/ shows featured posts
routes:
/featured/:
template: featured
controller: channel
filter: featured:true
limit: 12
This creates a paginated listing of all featured posts at /featured/, using featured.hbs.
Custom Collections
Collections are like channels but with permalink patterns.
collections:
/news/:
permalink: /news/{slug}/
template: news
filter: tags:[news]
limit: 10
/tutorials/:
permalink: /tutorials/{slug}/
template: tutorials
filter: tags:[tutorial]
limit: 6
Collection Options
| Option | Description |
|---|---|
permalink |
URL pattern for individual posts in this collection |
template |
Template for the collection listing |
filter |
Content filter |
limit |
Posts per page |
order |
Sort order |
Permalink Patterns
Use {slug} in permalinks to include the post slug:
# Post URL: /tutorials/getting-started/
permalink: /tutorials/{slug}/
You can also include the date:
# Post URL: /news/2024/01/my-post/
permalink: /news/{year}/{month}/{slug}/
Custom Taxonomies
Override the default tag and author URL patterns:
taxonomies:
tag: /topic/{slug}/
author: /writer/{slug}/
This changes:
- Tag page URLs from
/tag/<a href="/programming-languages/javascript/">JavaScript</a>/to/topic/javascript/ - Author page URLs from
/author/jane/to/writer/jane/
RSS Feeds for Custom Collections
Custom collections automatically get their own RSS feeds:
/news/rss/
/tutorials/rss/
/featured/rss/
Redirects in routes.yaml
You can define redirects for moved content:
redirects:
/old-url/: /new-url/
/old-category/: /new-category/
/legacy-post/: /blog/legacy-post/
Or use a separate redirects.yaml file at content/settings/redirects.yaml:
301:
/old-article/: /new-article/
/category/*: /blog/:splat/
302:
/temporary-link/: /new-location/
Advanced Routing Examples
Multi-Section Magazine
routes:
/featured/:
template: featured
controller: channel
filter: featured:true
limit: 10
collections:
/news/:
permalink: /news/{slug}/
template: index
filter: tags:[news]
limit: 10
/opinion/:
permalink: /opinion/{slug}/
template: index
filter: tags:[opinion]
limit: 10
taxonomies:
tag: /topic/{slug}/
Podcast Site
collections:
/episodes/:
permalink: /episodes/{slug}/
template: podcast
filter: tags:[podcast]
limit: 12
routes:
/about/:
template: page-about
data: page.about
Documentation Site
collections:
/docs/:
permalink: /docs/{slug}/
template: docs
filter: tags:[documentation]
limit: 50
taxonomies:
tag: /docs/topic/{slug}/
Template File Locations
Ghost looks for template files in this order:
- The specified
templatename (e.g.,featured.hbs) - If not found,
index.hbs - If not found, Ghost shows an error
Place custom templates in your theme root alongside the standard templates.
Reloading routes.yaml
After editing routes.yaml, restart Ghost:
ghost restart
Or use the Ghost CLI to reload routes without a full restart:
ghost reload
Common Mistakes
Forgetting to restart Ghost after editing routes.yaml: Changes to routes.yaml do not take effect until Ghost is restarted. Always run
ghost restartorghost reloadafter editing.Using absolute URLs in navigation instead of relative paths: Use
/about/nothttps://mysite.com/about/. Relative paths work regardless of domain changes and are required for subdirectory installations.Creating circular routes: If you create a route that matches all posts (
/blog/) and also define a collection for the same content, you can create infinite loops. Keep routes and collections distinct.Not testing custom routes before going live: A typo in routes.yaml can break your entire site structure. Test on a staging site before applying to production.
Overriding default taxonomies without updating templates: If you change
/tag/to/topic/in taxonomies, update your theme's tag links accordingly. Old hardcoded/tag/links will 404.
Practice Questions
What is the difference between
routesandcollectionsin routes.yaml? Answer:routesmap a single URL to a template (e.g., /about/).collectionsmap a URL prefix to a paginated content listing with individual post URLs (e.g., /news/ with posts at /news/{slug}/).How do you create a custom RSS feed for a specific content section? Answer: Define a custom collection or channel in routes.yaml. Ghost automatically generates an RSS feed for each collection at the collection URL + /rss/ (e.g., /news/rss/).
What happens if the template specified in routes.yaml does not exist? Answer: Ghost falls back to index.hbs for that route. If index.hbs also does not exist, Ghost returns an error. Always create the template files that your custom routes reference.
Challenge: Design a routes.yaml for a multi-section publication. Create routes for: a featured posts channel at /picks/, a tutorials collection at /tutorials/ with posts at /tutorials/{slug}/, a custom about page using page.about data, and custom taxonomy URLs at /topic/{slug}/ instead of /tag/{slug}/.
FAQ
Mini Project
Your task: Create a custom routes.yaml configuration for a magazine site.
- Define a custom route at /editors-picks/ showing featured posts with the template featured.hbs.
- Define a collection at /articles/ with posts at /articles/{slug}/, filtered by the "article" tag.
- Define a collection at /tutorials/ with posts at /tutorials/{slug}/, filtered by the "tutorial" tag.
- Override the tag taxonomy to use /topic/{slug}/.
- Create the corresponding template files (featured.hbs, with pagination).
- Create test posts with appropriate tags.
- Verify each URL works and shows the correct content.
This exercise gives you complete control over your site's URL structure and content organization.
What's Next
Now that you can customize themes, it is time to set up memberships:
Continue to Lesson 20: Membership System — Membership tiers, pricing, and Stripe integration setup.
Related lessons:
- Content Gating — Gate content behind membership levels
- Newsletter Setup — Configure email newsletters for members
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro