How to Build Your Own Custom Ghost Theme

In my last post, I explained how to install a new theme to your Ghost blog (assuming that you're able to find one you like). But, if you're picky or you want something unique, you'll probably want to build your own. This tutorial will get you started with the basics to build a custom Ghost theme.


If you don't already have Ghost installed locally on your machine, you should get that set up first. It will make developing and testing your theme much easier than uploading changes to a remote server. Here's a good article to get you going if you're working on a Mac. >> Installing Ghost on Mac

Theme Setup

First, create a folder for your theme and name it something original like my-theme :P. Place this folder in the content>themes directory of your Ghost app.

Ghost recommends using the following file structure for your theme:

├── /assets
|   └── /css
|       ├── screen.css
|   ├── /fonts
|   ├── /images
|   ├── /js
├── default.hbs
├── index.hbs [required]
└── post.hbs [required]
└── package.json [required]

The assets folder contains your css, fonts, images and js. The .hbs files are Handlebars.js templates that define layout of the different parts of your blog.


This is your theme's base template. It should contain the header and footer content that will be displayed on all pages of your blog. It's not required but it keeps you from needing to duplicate code across page templates.

Below is an example of what a very minimal default.hbs file might contain:

<!DOCTYPE html>
        {{! Page Meta }}
        <meta name="description" content="{{meta_description}}" />

        {{! Ghost outputs important style and meta data with this tag }}

    <body class="{{body_class}}">

        <div class="header">

        <div class="content">



Ghost Helpers

Ghost offers a number of Handlebars template helpers that give you access to data from your blog like metadata, the title of your blog, site navigation, posts and more.

You can take a look at this page of the Ghost docs for more information about the helpers you can use to output data in your templates. Its also helpful to inspect the source code of other Ghost themes you like to see how they make their stuff work. Here's a link to the code for Ghost's default theme Casper.

{{! Page Meta }} is a Handlebars template comment. Regular HTML comments <!-- like this --> can cause parse errors when the template renders sometimes so its best to avoid using them if possible.

{{meta_title}} and {{meta_description}} do what you would expect. They plug in your site's metadata. Ghost offers more metadata helpers than just these so make sure you reference the docs.

{{ghost_head}} outputs additional style and meta information. Its mainly Facebook and Twitter specific meta tags to help your post display nicely on social media. Thanks Ghost!

{{body_class}} will apply different classes depending on which page of the blog you're on. For example, the home page will get the home-template class and the posts page will get the post-template class and other "tag" related classes.

The @blog helper gives you access to information about your blog. In the example above, I used {{@blog.title}} to display the title of my blog in the header of my home page.

{{navigation}} outputs your site's navigation links.

{{! default Ghost navigation template }}
<ul class="nav">
    {{#foreach navigation}}
    <li class="nav-{{slug}}{{#if current}} nav-current{{/if}}" role="presentation"><a href="{{url absolute="true"}}">{{label}}</a></li>

To use it, you will need to create a navigation.hbs template file. Make sure to place it in a partials directory in the root of your theme folder (my-theme > partials > navigation.hbs).

{{{body}}} is where all of your other page templates get injected.

I'm not entirely sure what {{ghost_foot}} is used for at this time. I haven't seen it produce anything in my blogs so far. I've just left it in there in case it decides to make itself useful.

Your theme will work even if you don't include any of these helpers in your default.hbs file, but it will just display your blog as a single static page and what would be the point of that?


This is your theme's home page template. It can be used to list all of your posts and display content that you want to show up only on the front page of your blog.

Below is an example of what a very minimal index.hbs file might contain:

{{!< default}}
{{! The tag above means - insert everything in this file into the {body} of the default.hbs template }}

{{! Page Header}}
<header style="background-image: url('{{asset "/images/home-bg.jpg"}}')">
          <div class="site-heading">
                    <span class="subheading">{{@blog.description}}</span>

{{! Main Content}}
<div class="main-content">
          {{> "loop"}}

{{!< default}} is required to be placed at the top of your index.hbs file in order for it to be injected into the {{{body}}} section of your default.hbs file. If its not there, the content won't show up.

In the above example, I used {{asset "/images/home-bg.jpg"}} to define the url of my home page header image. This is the syntax you will want to use to include any files from your assets folder in your template files.

Again, in this template I used the @blog helper to inlcude my blog title and description in the template.

The Ghost Post Loop

{{> "loop"}} is used to list your published posts.

Just as with the navigation template, you will need to create a loop.hbs template partial. You'll place it in a partials directory in the root of your theme folder with your navigation.hbs (my-theme > partials > loop.hbs).


This is your theme's post template and controls how the individual articles of your blog look.

Below is an example of what a very minimal post.hbs file might contain:

{{!< default}}
<article class="{{post_class}}">

     {{! Everything inside the #post tags pulls data from the post }}
     <section class="post-content">
          <h1 class="post-title">{{{title}}}</h1>

     <footer class="post-footer">

     <section class="author">
          <h4>Written by</h4>



Again, as with the index.hbs file, you will need to place {{!< default}} at the top of your post.hbs file in order for it to be injected into the default template properly.

{{post_class}} sets different classes depending on the selected post.

{{#post}} and {{/post}} surround the content that will display data from the selected post.

{{{title}}} will display the post title.

{{content}} displays the post content. This is the stuff that you compose in the Ghost markdown editor.

author is a helper that gives you access to data about the author of the post. In the above example I used {{}} to display the name of the post's author.


Well, those are the basics. I hope this tutorial helps you to build a custom Ghost theme. Stay tuned for more in depth Ghost tutorials and articles!

I hope you enjoyed this post. Please leave a comment below if you have any questions or feedback.

About Author: Cassandra Wilcox
Cassandra Wilcox

Cassandra Wilcox is a full-stack JavaScript developer, Cofounder at Code Hangar Inc., Instructor at Girls Who Code, JavaScript Instructor at Skillcrush, and Organizer of Orlando LadyDevs