When I started customizing my Ghost theme, I quickly realized how repetitive image handling could be. Writing <img> tags manually for every post wasn’t scalable — especially if I wanted AVIF/WebP, responsive sizes, and lazy loading.

So, I decided to build a reusable image partial in Ghost using a Handlebars partial. Here’s how it works and how you can adapt it to your own theme.


🧩 The Concept

Ghost themes use Handlebars (.hbs) templates. You can define reusable components as partials, then include them anywhere with:

{{> image src=feature_image alt=feature_image_alt class="rounded-xl"}}

This avoids repetition and ensures consistency in your theme design.


💡 The Code Explained

Here’s the reusable image component (partials/image.hbs):

{{!--
Reusable image template for Ghost themes
Params:
  - src (required)
  - alt (optional)
  - class (optional)
  - loading (optional)
  - sizes (optional)
  - default_src_size (optional, defaults to "xl")
  - width / height (optional)
  - decoding (optional)
  - fetchpriority (optional)
  - draggable (optional)
  - animated (optional)
  - excluded_src_sizes (optional, bracket-separated list: e.g. "[xxs][xs]")
Example usage:
{{> image
    src=feature_image
    alt=feature_image_alt
    class="rounded-xl"
    loading="lazy"
    sizes="(max-width: 768px) 100vw, 768px"
    excluded_src_sizes="[xxs][xs]"
}}
--}}

{{#if src}}
  <picture{{#if class}} class="{{class}}"{{/if}}>
      {{#unless animated}}
      <source
          srcset="
              {{^match excluded_src_sizes "~" "[xxs]"}}{{img_url src size="xxs" format="avif"}} 30w,{{/match}}
              {{^match excluded_src_sizes "~" "[xs]"}}{{img_url src size="xs" format="avif"}} 150w,{{/match}}
              {{^match excluded_src_sizes "~" "[s]"}}{{img_url src size="s" format="avif"}} 300w,{{/match}}
              {{^match excluded_src_sizes "~" "[m]"}}{{img_url src size="m" format="avif"}} 720w,{{/match}}
              {{^match excluded_src_sizes "~" "[l]"}}{{img_url src size="l" format="avif"}} 960w,{{/match}}
              {{^match excluded_src_sizes "~" "[xl]"}}{{img_url src size="xl" format="avif"}} 1200w,{{/match}}
              {{^match excluded_src_sizes "~" "[xxl]"}}{{img_url src size="xxl" format="avif"}} 2000w,{{/match}}
              {{img_url src}}"
          {{#if sizes}}sizes="{{sizes}}"{{/if}}
          type="image/avif">
      {{/unless}}

      <source
          srcset="
              {{^match excluded_src_sizes "~" "[xxs]"}}{{img_url src size="xxs" format="webp"}} 30w,{{/match}}
              {{^match excluded_src_sizes "~" "[xs]"}}{{img_url src size="xs" format="webp"}} 150w,{{/match}}
              {{^match excluded_src_sizes "~" "[s]"}}{{img_url src size="s" format="webp"}} 300w,{{/match}}
              {{^match excluded_src_sizes "~" "[m]"}}{{img_url src size="m" format="webp"}} 720w,{{/match}}
              {{^match excluded_src_sizes "~" "[l]"}}{{img_url src size="l" format="webp"}} 960w,{{/match}}
              {{^match excluded_src_sizes "~" "[xl]"}}{{img_url src size="xl" format="webp"}} 1200w,{{/match}}
              {{^match excluded_src_sizes "~" "[xxl]"}}{{img_url src size="xxl" format="webp"}} 2000w,{{/match}}
              {{img_url src}}"
          {{#if sizes}}sizes="{{sizes}}"{{/if}}
          type="image/webp">

      <img
          srcset="
              {{^match excluded_src_sizes "~" "[xxs]"}}{{img_url src size="xxs"}} 30w,{{/match}}
              {{^match excluded_src_sizes "~" "[xs]"}}{{img_url src size="xs"}} 150w,{{/match}}
              {{^match excluded_src_sizes "~" "[s]"}}{{img_url src size="s"}} 300w,{{/match}}
              {{^match excluded_src_sizes "~" "[m]"}}{{img_url src size="m"}} 720w,{{/match}}
              {{^match excluded_src_sizes "~" "[l]"}}{{img_url src size="l"}} 960w,{{/match}}
              {{^match excluded_src_sizes "~" "[xl]"}}{{img_url src size="xl"}} 1200w,{{/match}}
              {{^match excluded_src_sizes "~" "[xxl]"}}{{img_url src size="xxl"}} 2000w,{{/match}}
              {{img_url src}}"
          {{#if sizes}}sizes="{{sizes}}"{{/if}}
          src="{{#if default_src_size}}{{img_url src size=default_src_size}}{{else}}{{img_url src size="xl"}}{{/if}}"
          {{#if alt}}alt="{{alt}}"{{/if}}
          {{#if class}}class="{{class}}"{{/if}}
          {{#if loading}}loading="{{loading}}"{{/if}}
          {{#if decoding}}decoding="{{decoding}}"{{/if}}
          {{#if fetchpriority}}fetchpriority="{{fetchpriority}}"{{/if}}
          {{#if draggable}}draggable="{{draggable}}"{{/if}}
          {{#if width}}width="{{width}}"{{/if}}
          {{#if height}}height="{{height}}"{{/if}}>
  </picture>
{{/if}}

It uses the <picture> tag to provide multiple formats (AVIF, WebP, fallback JPG/PNG).
Ghost automatically generates responsive image sizes, so your visitors only download what they need.

<picture>: The Picture element - HTML | MDN
The <picture> HTML element contains zero or more <source> elements and one <img> element to offer alternative versions of an image for different display/device scenarios.

⚙️ Optional Parameters

The snippet supports:

  • excluded_src_sizes → skip certain image sizes
  • sizes → control how images behave across screen widths
  • loading, fetchpriority, and decoding → performance hints
  • animated → skip AVIF for GIFs

You can call it anywhere in your theme, for example inside post.hbs or card.hbs.


🧠 Why This Matters

A reusable image component:

  • Simplifies your theme code
  • Improves Lighthouse performance scores
  • Ensures every image uses best practices automatically

Once you set it up, you never have to worry about srcset again.


☕ Personal Reflection

At first, I thought performance tweaks like this were overkill. But after seeing the difference in loading speed — especially on mobile — I realized small details add up.
Coding is like brewing coffee: take time, refine the process, and the result feels rewarding.

Reusable image template for Ghost themes
Reusable image template for Ghost themes. GitHub Gist: instantly share code, notes, and snippets.

Building a Reusable Image Partial for Ghost Themes

Learn how to build a reusable, optimized image partial in Ghost using Handlebars. We’ll explore how it works, how to customize it, and why it matters for performance.

Building a Reusable Image Partial for Ghost Themes