Development 16 min read

Ghost Theme Development: A Complete Guide for Beginners and Advanced Developers

Ghost Theme
Ghost Theme June 24, 2026
Ghost Theme Development: A Complete Guide for Beginners and Advanced Developers

If you have been exploring the world of Ghost CMS, you already know it is one of the cleanest, fastest publishing platforms available today. But here is where things get really interesting: Ghost themes. Building a custom Ghost theme from scratch is one of the most rewarding things you can do as a web developer. You get full creative control, performance that most WordPress themes simply cannot match, and a codebase that is genuinely enjoyable to work with.

This guide covers everything you need to know about Ghost theme development, from setting up your local environment all the way through to deploying a polished, production-ready theme. Whether you are just starting out or you have been working with Ghost for a while and want to go deeper, this is the resource I wish I had when I first started.


1. What Is Ghost and Why Build Custom Themes?

Ghost is an open-source publishing platform built specifically for professional content creators, bloggers, and online publications. It was launched in 2013 as a Kickstarter project and has since grown into a full-featured platform with built-in membership, subscription, and newsletter tools.

Unlike WordPress, Ghost is purpose-built for publishing. There are no plugins doing a hundred different things. The codebase is lean, the admin panel is distraction-free, and the front end is entirely yours to shape through theming.

Why build a custom Ghost theme instead of buying one?

A few honest reasons:

You get exactly what you need. Marketplace themes are built to satisfy as many buyers as possible. That means they ship with dozens of layout options, color switchers, and settings you will never use. A custom theme has zero bloat.

Performance is significantly better. When you write the theme yourself, you only load the CSS and JavaScript your design actually needs. This translates directly to faster page loads, better Core Web Vitals scores, and improved SEO.

You understand every line of code. When something breaks or a client asks for a specific change, you are not digging through someone else's minified, poorly documented stylesheet. You know exactly where to go.

It is a genuinely marketable skill. Ghost theme developers are in consistent demand. If you want to sell themes or offer Ghost development as a freelance service, building custom themes from scratch gives you the expertise and portfolio to charge premium rates.


2. Understanding the Ghost Theme Architecture

Before writing a single line of code, it helps to understand how Ghost themes are structured. Ghost themes follow a convention-over-configuration approach, which means the platform expects certain files to be in certain places.

A Ghost theme is essentially a folder containing Handlebars template files, CSS, JavaScript, and a package.json file. That is the entire structure. When you zip that folder and upload it to Ghost, the platform reads the templates and renders your theme for visitors.

Here is what a typical Ghost theme folder looks like:

my-ghost-theme/
├── assets/
│   ├── css/
│   │   └── screen.css
│   ├── js/
│   │   └── main.js
│   └── images/
├── partials/
│   ├── header.hbs
│   ├── footer.hbs
│   └── post-card.hbs
├── default.hbs
├── index.hbs
├── post.hbs
├── page.hbs
├── tag.hbs
├── author.hbs
├── error.hbs
├── package.json
└── README.md

The .hbs extension stands for Handlebars, the templating language Ghost uses. Each file represents a different part of your site. Ghost determines which template to render based on the URL and content type being viewed.

The template hierarchy

Ghost uses a template hierarchy similar to WordPress but much simpler. Here is how it resolves templates from most specific to least specific:

  • Custom templates (e.g., custom-landing-page.hbs) for specific pages
  • Content-type templates (e.g., post.hbspage.hbstag.hbs)
  • Index template (index.hbs) for the homepage and listing pages
  • Default template (default.hbs) as the layout wrapper for everything

Understanding this hierarchy early on saves you a lot of confusion later.


3. Setting Up Your Local Development Environment

You do not want to develop your theme directly on a live Ghost site. You need a local environment. Ghost makes this straightforward with Ghost CLI.

What you need installed first

  • Node.js (Ghost requires a specific LTS version, check the Ghost docs for the current requirement)
  • npm (comes with Node.js)
  • Ghost CLI (npm install ghost-cli -g)

Installing Ghost locally

Once you have Ghost CLI installed:

# Create a directory for your local Ghost install
mkdir my-ghost-site
cd my-ghost-site

# Install Ghost in development mode
ghost install local

This installs a local Ghost instance and starts it up. You can access the admin panel at http://localhost:2368/ghost and the front end at http://localhost:2368.

Linking your theme for development

Navigate to the content/themes folder inside your Ghost installation and either create your theme folder there directly or create a symlink to your theme folder elsewhere on your machine. Ghost automatically detects themes in this directory.

After adding your theme, activate it from the Ghost admin under Settings > Design.

Using live reload during development

For a smoother development workflow, most Ghost developers use a tool like Gulp or Browsersync alongside their CSS preprocessor of choice. Ghost's own Starter theme uses Gulp with a watch task that rebuilds your CSS and reloads the browser on every save.

You can also enable Ghost's native development mode, which disables caching and makes template changes reflect immediately without needing to restart.


4. The Handlebars Templating System in Ghost

Ghost uses Handlebars.js as its templating engine. If you have worked with templating languages before, Handlebars will feel familiar. If you have not, do not worry. The learning curve is gentle.

Handlebars uses double curly braces for expressions: {{expression}}. You use these to output dynamic data from Ghost into your HTML.

Basic Handlebars syntax

Output a value:

<h1>{{title}}</h1>

Block helpers:

{{#if featured}}
  <span class="featured-label">Featured</span>
{{/if}}

Iterating over a list:

{{#foreach posts}}
  <article>
    <h2>{{title}}</h2>
    <p>{{excerpt}}</p>
  </article>
{{/foreach}}

Including a partial:

{{> header}}

The body class helper

One small but useful Handlebars feature in Ghost is the {{body_class}} helper. It outputs CSS classes on the <body> tag that describe the current page context, like home-templatepost-templatetag-template, and so on. This makes it easy to write context-specific CSS without duplicating templates.

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

5. Core Template Files You Need to Know

Let us go through the most important template files in a Ghost theme and what each one does.

default.hbs

This is your main layout wrapper. Think of it as the shell that every other template lives inside. It typically contains your <html><head><body> tags, global navigation, and footer. Other templates inject their content into default.hbs using the {{{body}}} helper.

<!DOCTYPE html>
<html lang="{{@site.locale}}">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{{meta_title}}</title>
  {{ghost_head}}
</head>
<body class="{{body_class}}">

  {{> header}}

  {{{body}}}

  {{> footer}}

  {{ghost_foot}}
</body>
</html>

The {{ghost_head}} and {{ghost_foot}} helpers are essential. They inject Ghost's required meta tags, scripts, and member authentication code. Never leave them out.

index.hbs

This template renders the homepage and all paginated listing pages (like tag archives or author pages, unless they have their own templates). It receives a posts context that you loop through with {{#foreach posts}}.

{{!< default}}

<main class="site-main">
  <div class="post-grid">
    {{#foreach posts}}
      {{> post-card}}
    {{/foreach}}
  </div>

  {{pagination}}
</main>

The {{!< default}} at the top tells Ghost to wrap this template inside default.hbs.

post.hbs

This renders individual blog posts. It receives a single post context with all the post data: title, content, author, tags, feature image, and more.

{{!< default}}

<article class="{{post_class}}">
  {{#post}}
    <header class="post-header">
      <h1 class="post-title">{{title}}</h1>
      <div class="post-meta">
        <time datetime="{{date format="YYYY-MM-DD"}}">{{date format="D MMMM YYYY"}}</time>
        {{> author-card}}
      </div>
    </header>

    {{#if feature_image}}
      <figure class="post-image">
        <img src="{{feature_image}}" alt="{{title}}">
      </figure>
    {{/if}}

    <div class="post-content kg-canvas">
      {{content}}
    </div>
  {{/post}}
</article>

page.hbs

Pages work similarly to posts but are typically used for static content like About pages or Contact pages. If you do not create a page.hbs, Ghost falls back to post.hbs.

tag.hbs

This renders tag archive pages. If absent, Ghost falls back to index.hbs. You often want a separate tag.hbs so you can show the tag's name, description, and feature image at the top.

author.hbs

Similar to tag.hbs but for author profile pages. A good author page shows the author's bio, profile picture, social links, and their posts.

error.hbs

This handles 404 and other error pages. It is worth creating a friendly error page with a search box or link back to the homepage rather than showing Ghost's default error screen.


6. Ghost Theme Assets: CSS, JavaScript, and Fonts

All static assets go inside the assets/ folder. Ghost serves them at the path /assets/ automatically.

Referencing assets in templates

Use the {{asset}} helper to output correct asset paths:

<link rel="stylesheet" href="{{asset "css/screen.css"}}">
<script src="{{asset "js/main.js"}}"></script>

This helper appends a cache-busting query string automatically, which is a nice built-in feature.

CSS approaches for Ghost themes

You have several options for writing your theme's styles:

Plain CSS works perfectly well and is often the right choice for simpler themes. Modern CSS with custom properties (variables), grid, and flexbox is powerful enough for any layout.

Sass/SCSS is popular for larger themes. Many Ghost developers (myself included) prefer Sass because it keeps styles organized with nesting, variables, and mixins. You compile it down to a single screen.css file using a build tool.

Tailwind CSS is an increasingly popular option. Some Ghost theme developers use Tailwind's utility classes directly in their Handlebars templates. This requires a build step to purge unused classes, but the output is extremely lean.

Handling the Ghost Content API styles

Ghost's editor (called Koenig) outputs HTML with specific class names for different card types: galleries, bookmarks, callouts, videos, audio, and more. These classes follow the kg- prefix convention (e.g., kg-cardkg-image-cardkg-gallery-card).

You need to write CSS for these classes in your theme, or your post content will look unstyled. Ghost provides a free CSS file called Ghost Koenig styles that you can use as a starting point. Many developers include it as a base and customize from there.


7. Working with Ghost Helpers

Ghost adds a large set of custom Handlebars helpers on top of the standard ones. Here are the ones you will use most often.

Data helpers

{{@site}} gives you access to global site settings like the site title, description, logo, accent color, and locale:

<a href="{{@site.url}}">{{@site.title}}</a>

{{@config}} exposes configuration values. This is less commonly used in themes directly.

{{@member}} is essential if your theme supports Ghost memberships. It gives you the logged-in member's details.

URL and navigation helpers

{{url}} outputs the full URL of the current post or page when used inside a block context.

{{navigation}} renders the primary navigation menu configured in Ghost admin. You can customize the output using a navigation partial or by passing it through a helper.

{{pagination}} renders next/previous page links on listing pages. You can write a custom partial for this if you want more control over the HTML output.

Content and formatting helpers

{{content}} outputs the full rendered HTML of a post. Always use triple braces {{{content}}} to avoid HTML escaping.

{{excerpt}} outputs a plain text excerpt. You can limit it by word count: {{excerpt words="50"}}.

{{date}} formats dates using Moment.js format strings: {{date format="DD MMMM YYYY"}}.

{{reading_time}} outputs an estimated reading time based on word count. Very useful for modern blog designs.

{{img_url}} is extremely powerful. It generates responsive image URLs using Ghost's built-in image processing:

<img src="{{img_url feature_image size="l"}}" 
     srcset="{{img_url feature_image size="s"}} 400w,
             {{img_url feature_image size="m"}} 750w,
             {{img_url feature_image size="l"}} 1200w"
     alt="{{title}}">

Ghost automatically creates multiple sizes of uploaded images, and img_url lets you reference them directly.

The {{#get}} helper

This is one of the most powerful helpers in Ghost. It lets you fetch additional data from the Ghost Content API directly inside a template, without writing any JavaScript.

{{#get "posts" limit="3" include="tags,authors"}}
  {{#foreach posts}}
    <h3>{{title}}</h3>
  {{/foreach}}
{{/get}}

You can filter, limit, and sort the results just like you would with the Content API directly. This is how you build things like "Related Posts" sections or featured post highlights in the sidebar.


8. Dynamic Routing in Ghost Themes

Ghost has a powerful routing system that lets you define custom URL structures and content collections. This is configured in a file called routes.yaml, which lives in Ghost's content/settings/ folder, but as a theme developer, you need to understand how it affects your templates.

Default routing behavior

Out of the box, Ghost routes content like this:

  • yoursite.com/ - Homepage (index.hbs)
  • yoursite.com/post-slug/ - Posts (post.hbs)
  • yoursite.com/page-slug/ - Pages (page.hbs)
  • yoursite.com/tag/tag-slug/ - Tags (tag.hbs)
  • yoursite.com/author/author-slug/ - Authors (author.hbs)

Custom routes with routes.yaml

routes.yaml lets you remap these completely. You can create a blog at /blog/ instead of the root, build a portfolio section at /work/, or create a custom landing page at /newsletter/. Here is a simple example:

routes:
  /custom-landing/:
    template: custom-landing
    data: page.my-landing-page

collections:
  /blog/:
    permalink: /blog/{slug}/
    template: index
    filter: tag:blog

  /tutorials/:
    permalink: /tutorials/{slug}/
    template: index
    filter: tag:tutorial

taxonomies:
  tag: /tag/{slug}/
  author: /author/{slug}/

This kind of routing is what powers some of the most sophisticated Ghost sites out there.


9. Ghost Theme Settings and Custom Fields

One of the features added in recent versions of Ghost is the ability to define custom theme settings that site owners can configure from the Ghost admin, without touching code.

These are defined in your package.json file:

{
  "name": "my-ghost-theme",
  "version": "1.0.0",
  "engines": {
    "ghost": ">=5.0.0"
  },
  "config": {
    "posts_per_page": 12,
    "image_sizes": {
      "xxs": { "width": 30 },
      "xs": { "width": 100 },
      "s": { "width": 300 },
      "m": { "width": 600 },
      "l": { "width": 1000 },
      "xl": { "width": 2000 }
    }
  },
  "custom": {
    "navigation_layout": {
      "type": "select",
      "options": ["Logo on the left", "Logo in the middle", "Stacked"],
      "default": "Logo on the left"
    },
    "show_featured_posts": {
      "type": "boolean",
      "default": true,
      "group": "homepage"
    },
    "footer_text": {
      "type": "text",
      "default": "Built with Ghost"
    }
  }
}

In your templates, you access these settings through the @custom data accessor:

{{#match @custom.navigation_layout "Logo in the middle"}}
  <nav class="nav-centered">...</nav>
{{/match}}

This feature is a game-changer for building themes you intend to sell or distribute. Instead of requiring buyers to edit code, they can configure the theme visually from the admin panel.


10. Membership and Subscription Features in Themes

Ghost has built-in membership and newsletter features. If your theme needs to support these (and most modern Ghost themes should), there are several template considerations.

Member authentication helpers

The {{@member}} data accessor lets you conditionally show content based on membership status:

{{#if @member}}
  <p>Welcome back, {{@member.name}}!</p>
{{else}}
  <a href="#/portal/signup">Subscribe for free</a>
{{/if}}

Portal triggers

Ghost's membership system uses a component called Portal, which handles sign-up, sign-in, and account management. You trigger Portal by adding specific hash URLs to links:

<!-- Open the signup modal -->
<a href="#/portal/signup">Subscribe</a>

<!-- Open the signin modal -->
<a href="#/portal/signin">Sign in</a>

<!-- Open account management -->
<a href="#/portal/account">Your account</a>

Access restriction for paid content

Posts can be restricted to free members, paid members, or public. Ghost automatically handles the paywall enforcement, but your theme controls how the locked content is visually presented. When a post is paywalled, Ghost replaces the content with an <aside class="gh-post-upgrade-cta"> element. Style this element to create a visually compelling upgrade prompt.

Member count and subscription context

{{#if @site.members_enabled}}
  <div class="subscribe-cta">
    Join {{@site.member_count}} readers
  </div>
{{/if}}

11. Testing and Validating Your Ghost Theme with GScan

Before uploading or distributing your theme, you should run it through GScan, Ghost's official theme compatibility and validation tool.

Running GScan

You can run GScan as a CLI tool:

npm install -g gscan
gscan /path/to/your/theme

Or you can use the online version at gscan.ghost.org by uploading a zipped version of your theme.

What GScan checks

GScan validates your theme against the current Ghost theme specification. It checks for:

  • Required files (like index.hbs and post.hbs)
  • Correct use of required Handlebars helpers ({{ghost_head}}{{ghost_foot}}, etc.)
  • Template compatibility with the current Ghost version
  • Deprecated helpers or patterns that will cause issues
  • package.json validity

GScan categorizes issues as errors (must fix before uploading), warnings (should fix), and recommendations (nice to fix). Always resolve all errors and as many warnings as possible before considering your theme complete.


12. Deploying Your Ghost Theme

When your theme is ready and passes GScan validation, deploying it is simple.

Zipping your theme

Create a zip file of your theme folder. The zip should contain the theme files directly, not a parent folder. So when you open the zip, you should see index.hbspackage.jsonassets/, etc. at the root level.

On a Mac or Linux:

cd /path/to/your-theme
zip -r ../your-theme.zip . -x "*.git*" -x "node_modules/*" -x ".DS_Store"

Uploading to Ghost admin

In Ghost admin, go to Settings > Design > Change theme > Upload theme. Select your zip file and Ghost will install and activate it.

Updating an existing theme

When you upload a theme with the same name as an existing one, Ghost will replace it. Your settings and routes configuration are preserved.

Version control and CI/CD

For professional deployments, many developers connect their Ghost installation to a Git workflow. Ghost Pro (the managed hosting) supports direct theme uploads from the admin. Self-hosted Ghost installations can be set up with scripts that sync the theme folder from a Git repository automatically on commit.


13. Performance Best Practices for Ghost Themes

One of the reasons people choose Ghost over other platforms is performance. Your theme should never be the bottleneck. Here are the practices that matter most.

Use native image lazy loading

<img src="{{feature_image}}" alt="{{title}}" loading="lazy">

This one attribute defers off-screen images from loading until the user scrolls near them. It is natively supported in all modern browsers and requires zero JavaScript.

Serve responsive images

Always use the {{img_url}} helper with size parameters and a proper srcset. Let the browser pick the right resolution for the device.

Minimize JavaScript

Ghost themes often need very little JavaScript. Resist the temptation to include jQuery, large UI component libraries, or unnecessary animations. If a feature can be achieved with CSS, use CSS.

Preload critical assets

Add preload hints in default.hbs for your most important assets:

<link rel="preload" href="{{asset "css/screen.css"}}" as="style">
<link rel="preload" href="{{asset "fonts/my-font.woff2"}}" as="font" type="font/woff2" crossorigin>

Use system fonts or variable fonts thoughtfully

Loading multiple font weights from Google Fonts or another CDN adds up fast. Consider using a system font stack for body text, or use a single variable font file that covers multiple weights.

Avoid render-blocking resources

Place non-critical JavaScript at the bottom of default.hbs before the closing </body> tag, or use defer and async attributes on script tags.

Test with real tools

Run your theme through PageSpeed Insights and WebPageTest regularly during development. Look specifically at Core Web Vitals: Largest Contentful Paint (LCP), Cumulative Layout Shift (CLS), and Interaction to Next Paint (INP).


14. Common Ghost Theme Development Mistakes to Avoid

After spending a lot of time in the Ghost theme ecosystem, I have seen the same mistakes come up repeatedly. Save yourself the headache.

Forgetting {{ghost_head}} and {{ghost_foot}}

These are required. {{ghost_head}} outputs meta tags for SEO, Open Graph, structured data, and CSS from Ghost admin customizations. {{ghost_foot}} outputs the member authentication scripts. Without them, major features simply will not work.

Not styling Koenig card classes

Writers will use Ghost's editor to add galleries, callouts, bookmarks, NFT cards, buttons, and all sorts of content types. If your theme does not have CSS for kg-* classes, post content will look broken for anything beyond basic text.

Hardcoding site URLs

Never hardcode your site's domain anywhere in a Ghost theme. Use {{@site.url}} instead. Themes are meant to be portable between environments.

Not testing with actual Ghost content

Develop with real-looking content in your local Ghost install. If you only test with a couple of short posts, you will miss layout issues that appear with long titles, very long posts, posts without feature images, posts with multiple tags, and so on. Ghost's demo content (populated when you first set up a local install) is a good starting point.

Ignoring the {{post_class}} helper

This helper outputs useful CSS classes on the <article> element: whether the post is featured, whether it has a feature image, which tags it has, and more. Use it and write CSS against it for a much more flexible design system.

Not accounting for right-to-left (RTL) languages

If there is any chance your theme will be used for non-English content, add basic RTL support. Setting dir="{{@site.text_direction}}" on the <html> tag and writing mirrored CSS using [dir="rtl"] selectors goes a long way.


15. Resources and Next Steps

Ghost theme development has a genuinely friendly and active community. Here are the resources worth bookmarking.

Official resources

  • Ghost Theme Documentation at ghost.org/docs/themes/ is the authoritative reference. Keep it open while you develop.
  • GScan at gscan.ghost.org validates your theme against the current spec.
  • Ghost Forum at forum.ghost.org is where the community hangs out. Developers are generally helpful and the Ghost core team is active there.

Starter themes and references

The best way to learn Ghost theme development is to read well-written theme code. Ghost maintains several official starter themes:

  • Casper is the default Ghost theme and represents current best practices. It is worth reading through even if you are not going to use it directly.
  • Source is Ghost's open-source starter theme built specifically to be a customization starting point.
  • Dawn, Alto, Journal, Edition are Ghost's official premium themes and show how to handle memberships, newsletters, and various layout approaches.

All of these are open source and available on the Ghost GitHub organization.

Learning from real Ghost developers

If you are looking for real-world Ghost theme development work, take a look at what experienced developers are building. One developer worth following is Anisul Kibria at Anisul.com, a Ghost theme developer who shares insights on building high-quality, production-ready Ghost themes. Looking at how experienced developers structure their code and approach design problems is one of the fastest ways to level up.

Practice project ideas

If you want to build your skills deliberately, here are some theme project ideas ordered from simpler to more complex:

  • Minimal blog theme with just typography, spacing, and no extra features. Forces you to nail the fundamentals.
  • Magazine-style theme with a featured hero, post grid, and sticky navigation.
  • Membership-focused theme that incorporates Portal, member-only content sections, and a subscription CTA design.
  • Portfolio theme using custom routes to create a /work/ section powered by Ghost pages.
  • Newsletter theme where the visual design mirrors the email newsletter experience.

Wrapping Up

Ghost theme development sits in a great place right now. The platform is stable and growing, the theming API is mature and well-documented, and there is real demand for quality custom themes from individuals and publications that have moved beyond generic marketplace options.

The learning curve is real but not steep. If you know HTML, CSS, and have a basic understanding of how templating languages work, you can build a functional Ghost theme in a weekend and a polished, production-ready one in a few weeks of focused work.

The most important thing is to start. Spin up a local Ghost install today, grab the Casper source from GitHub to read through, and start building. The Ghost community is welcoming and the documentation is genuinely good. You have everything you need.

If you found this guide useful and want to go deeper, bookmark this site and check back regularly. Ghost theme development is one of those skills that rewards continuous learning, and there is always more to explore.


Ghost Theme

Written by

Ghost Theme

View all posts →

Keep reading

More like this.

Build something beautiful

Your dream publication, today.

Join 5,000+ creators who trust our themes. Get every theme — plus all future releases — for one simple price.

30-day money back guarantee · Cancel anytime