Skip to content

Ghost Theme Basics — default.hbs, post.hbs, page.hbs and Partials

DodaTech Updated 2026-06-28 9 min read

In this tutorial, you'll learn the basics of Ghost theme development — understanding the template hierarchy, creating default.hbs, post.hbs, and page.hbs templates, using partials for reusable components, and how themes render different content types.

What You'll Learn

  • The Ghost theme template hierarchy and how Ghost chooses templates
  • Creating the required default.hbs file
  • post.hbs: The template for individual post pages
  • page.hbs: The template for static pages
  • Partials: reusable template components (header, footer, navigation)
  • The theme directory structure and required files
  • Template contexts and available data
  • How Ghost renders different routes with different templates

Why It Matters

Themes control every aspect of how your content looks. Even if you use a pre-built theme, understanding the template hierarchy helps you customize it. When you want to change how your post page looks, you edit post.hbs. When you want to customize the homepage, you edit index.hbs. Knowing which file controls which part of your site turns theming from guesswork into engineering.

Real-World Use

A blogger wants to add an author bio section at the bottom of every post. Instead of editing the full post template, she opens post.hbs in her theme, adds a Handlebars partial for the author card, and the bio appears on every post. Later she wants a different layout for her "About" page — she creates a custom page template (page-about.hbs) with a full-width layout.

Learning Path

flowchart LR
  A["Image & Media"] --> B["Theme Basics
You are here"]:::current B --> C["Handlebars Templates"] C --> D["Theme Assets"] classDef current fill:#38bdf8,color:#0f172a,stroke-width:2px

Theme Directory Structure

A Ghost theme is a folder containing Handlebars templates, assets, and a package.json file.

my-theme/
├── assets/
│   ├── css/
│   │   └── style.css
│   └── js/
│       └── index.js
├── partials/
│   ├── header.hbs
│   └── footer.hbs
├── default.hbs
├── index.hbs
├── post.hbs
├── page.hbs
├── tag.hbs
├── author.hbs
├── error.hbs
└── package.json

Some files are required, others are optional.

Required Files

File Purpose
default.hbs The base template that all other templates inherit from
index.hbs The homepage and post listing
post.hbs Individual post pages
page.hbs Individual page views
package.json Theme metadata and configuration

Optional Files

File Purpose
tag.hbs Tag page (falls back to index.hbs if missing)
author.hbs Author page (falls back to index.hbs if missing)
error.hbs 404 and error pages (English default if missing)
custom-*.hbs Custom templates for specific routes
partials/*.hbs Reusable template components

The Template Hierarchy

When Ghost renders a page, it selects the template based on the route:

flowchart TD
  A["Request URL"] --> B{"Route type?"}
  B -->|"/"| C["index.hbs"]
  B -->|"/post-slug/"| D["post.hbs"]
  B -->|"/page-slug/"| E["page.hbs"]
  B -->|"/tag/tag-slug/"| F["tag.hbs"]
  B -->|"/author/author-slug/"| G["author.hbs"]
  B -->|"404"| H["error.hbs"]
  B -->|"/custom/"| I["custom-{route}.hbs"]

  C --> J["default.hbs (wraps everything)"]
  D --> J
  E --> J
  F --> J
  G --> J
  H --> J
  I --> J

  style J fill:#38bdf8,color:#0f172a

All templates are wrapped in default.hbs, which provides the HTML document structure.

default.hbs

default.hbs is the base template that contains the HTML structure shared by every page. It includes the <!DOCTYPE html>, <head>, and the Ghost {{body}} helper.

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

  {{ghost_head}}

  <link rel="stylesheet" href="{{asset "css/style.css"}}">
</head>
<body>

  {{> header}}

  <main>
    {{{body}}}
  </main>

  {{> footer}}

  {{ghost_foot}}

</body>
</html>

Key elements:

  • {{ghost_head}} — Ghost injects SEO metadata, JSON-LD structured data, and other head elements here.
  • {{ghost_foot}} — Ghost injects scripts and code injection here.
  • {{{body}}} — Three curly braces tell Handlebars not to escape HTML. This is where child templates render their content.
  • {{> header}} — Includes the header partial.
  • {{asset "css/style.css"}} — Generates the correct URL for the asset file.

index.hbs

index.hbs renders the homepage and paginated post listings. It is the default template for the root route and any route that shows a list of posts.

{{!< default}}

<section class="post-feed">
  {{#foreach posts}}
    <article class="post-card">
      {{#if feature_image}}
        <a href="{{url}}">
          <img src="{{img_url feature_image size="s"}}" alt="{{title}}">
        </a>
      {{/if}}
      <h2><a href="{{url}}">{{title}}</a></h2>
      <p>{{excerpt words="30"}}</p>
      <div class="post-meta">
        <time datetime="{{date format="YYYY-MM-DD"}}">{{date}}</time>
        {{#foreach tags}}
          <a href="{{url}}">{{name}}</a>
        {{/foreach}}
      </div>
    </article>
  {{/foreach}}
</section>

{{pagination}}

Key elements:

  • {{!< default}} — Tells Ghost to wrap this template inside default.hbs. The content replaces {{{body}}} in the parent template.
  • {{#foreach posts}} — Loops through all posts on the current page.
  • {{pagination}} — Renders pagination links (Previous/Next).

post.hbs

post.hbs renders individual post pages. It has access to the full post context.

{{!< default}}

<article class="post-full">
  {{#post}}
    {{#if feature_image}}
      <figure class="post-image">
        <img src="{{img_url feature_image size="xl"}}" alt="{{feature_image_alt}}">
        {{#if feature_image_caption}}
          <figcaption>{{feature_image_caption}}</figcaption>
        {{/if}}
      </figure>
    {{/if}}

    <h1 class="post-title">{{title}}</h1>

    <div class="post-meta">
      <time datetime="{{date format="YYYY-MM-DD"}}">{{date}}</time>
      {{#foreach authors}}
        <a href="{{url}}">{{name}}</a>
      {{/foreach}}
    </div>

    <section class="post-content">
      {{content}}
    </section>

    <div class="post-tags">
      {{#foreach tags}}
        <a href="{{url}}">{{name}}</a>
      {{/foreach}}
    </div>
  {{/post}}
</article>

The {{#post}} block sets the context to the current post. Inside it, you can access all post properties: title, content, feature_image, date, tags, authors, excerpt, reading_time, and more.

page.hbs

page.hbs renders static pages. It is identical in structure to post.hbs but used for pages instead of posts.

{{!< default}}

<article class="page-full">
  {{#post}}
    <h1>{{title}}</h1>
    <section class="page-content">
      {{content}}
    </section>
  {{/post}}
</article>

Some themes use the same template for posts and pages by not differentiating. However, separating them gives you more control — you might want a different header style, no author byline, or a different layout for pages.

Partials

Partials are reusable template snippets stored in the partials/ folder. They are included in other templates using the {{>}} syntax.

header.hbs

<header class="site-header">
  <div class="header-content">
    <h1 class="site-title">
      <a href="{{@site.url}}">{{@site.title}}</a>
    </h1>

    <nav class="site-nav">
      {{navigation}}
    </nav>

    <button class="menu-toggle">Menu</button>
  </div>
</header>

footer.hbs

<footer class="site-footer">
  <div class="footer-content">
    <p>&copy; {{date format="YYYY"}} <a href="{{@site.url}}">{{@site.title}}</a></p>
    <nav class="footer-nav">
      {{navigation type="secondary"}}
    </nav>
  </div>
</footer>

Subdirectory Partials

You can organize partials in subdirectories:

partials/
├── components/
│   ├── post-card.hbs
│   └── author-card.hbs
└── layout/
    ├── header.hbs
    └── footer.hbs

Include them with the subdirectory path:

{{> "components/post-card"}}
{{> "components/author-card"}}

Custom Templates

You can create custom page templates by naming them with the page slug. For example, if you have a page with slug "about," create page-about.hbs. Ghost automatically uses it for that page.

Custom templates follow this pattern:

  • page-{slug}.hbs — for pages
  • post-{slug}.hbs — for posts
  • tag-{slug}.hbs — for tags
  • author-{slug}.hbs — for authors

Or you can define custom routes in routes.yaml (Lesson 35).

Common Mistakes

  1. Forgetting {{!< default}} at the top of templates: Every template that is not default.hbs must start with {{!< default}} to inherit the base layout. Without it, the template renders without the HTML structure, head tags, or theme assets.

  2. Using two curly braces for body content: The {{{body}}} and {{content}} helpers use three curly braces ({{{ }}}). Two curly braces ({{ }}) escape HTML, which would render post content as unformatted text with visible HTML tags.

  3. Editing the default.hbs of a third-party theme without backing up: If you modify default.hbs and something breaks, you need to restore the original. Always back up your theme before editing.

  4. Missing the {{ghost_head}} helper: Without {{ghost_head}}, Ghost does not inject SEO metadata, canonical URLs, or JSON-LD structured data. Your pages will have missing SEO elements.

  5. Hardcoding URLs instead of using helpers: Using https://mysite.com/about instead of {{@site.url}}/about means you must update URLs if your site changes domain. Always use Ghost helpers for site URLs.

Practice Questions

  1. What are the three required files in a Ghost theme? Answer: default.hbs (base template), index.hbs (homepage/post listing), and post.hbs (individual post view). page.hbs is also typically considered a core file. package.json is required for theme metadata.

  2. What is the purpose of the {{!< default}} syntax in Ghost templates? Answer: It tells Ghost to render the current template's content inside the default.hbs layout. The content replaces the {{{body}}} helper in default.hbs. Without it, the template renders standalone without site chrome.

  3. How do you include a partial in a Ghost template? Answer: Use the {{> partial-name}} syntax. Partials are stored in the partials/ directory. For subdirectory partials, use the path: {{> "components/post-card"}}.

  4. Challenge: Create a minimal Ghost theme from scratch with default.hbs, index.hbs, post.hbs, page.hbs, and two partials (header and footer). Install the theme on a local Ghost site and verify it renders posts, pages, and the homepage correctly.

FAQ

What happens if a template file is missing?

Ghost falls back to index.hbs for most missing templates. If post.hbs is missing, Ghost uses index.hbs to render posts. If error.hbs is missing, Ghost shows its built-in error page. Always provide at least the required templates.

Can I use CSS frameworks like Tailwind in Ghost themes?

Yes. Ghost themes support any CSS framework. Include the framework's CSS file in your assets folder or link to a CDN. Tailwind can be compiled and included as a static CSS file.

How do I create a custom 404 page?

Create error.hbs in your theme root. Ghost uses it for 404 and other errors. The template has access to {{statusCode}} and {{message}} context variables to show different content for different errors.

Can I use JavaScript frameworks like React in Ghost themes?

Ghost themes are server-rendered with Handlebars. You can add JavaScript to your theme for interactivity, but the primary rendering is server-side. For a React frontend, use Ghost as a headless CMS (Lesson 29).

How do I debug a template that is not rendering correctly?

Enable Ghost's developer experiments in config to get verbose error output. Check the browser console for errors. Use the {{log}} helper to output variable values to the console during development.

Mini Project

Your task: Create a minimal Ghost theme and test it locally.

  1. Create a new folder called minimal-theme in content/themes/.
  2. Create default.hbs with HTML structure, ghost_head, ghost_foot, header partial, footer partial, and {{{body}}}.
  3. Create index.hbs that loops through posts and displays title, excerpt, and date.
  4. Create post.hbs that shows the full post with feature image, content, tags, and author.
  5. Create page.hbs similar to post.hbs but without author/tags.
  6. Create partials/header.hbs with site title and navigation.
  7. Create partials/footer.hbs with copyright.
  8. Create package.json with theme name and version.
  9. Select the theme in Ghost Admin > Settings > Design.
  10. Verify the theme works on all page types.

This exercise gives you a working theme skeleton you can extend for any project.

What's Next

Now that you understand the template structure, dive deeper into Handlebars:

Continue to Lesson 16: Handlebars Templates — foreach, if, helpers, contexts, and advanced templating.

Related lessons:

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro