Custom Ghost Theme Development on Handlebars Turnkey

Imagine: your media blog on Ghost lags at 5000 posts, pages load in 4 seconds, and Google sees LCP 4.2s. Typical for themes where nested `{{#get}}` requests without caching and unoptimized images. We solve this by rewriting the theme for Ghost 5 with custom settings and proper caching.

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1414
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1285
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    982
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1241
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    982
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Website development for FIXPER company
    994

Imagine: your media blog on Ghost lags at 5000 posts, pages load in 4 seconds, and Google sees LCP 4.2s. Typical for themes where nested {{#get}} requests without caching and unoptimized images. We solve this by rewriting the theme for Ghost 5 with custom settings and proper caching.

Unlike WordPress, Handlebars in Ghost doesn't execute arbitrary code — only calls built-in helpers. This is a plus for security but a minus for performance if you don't know the details. Our team with years of experience in Ghost development (30+ projects) creates themes that pass gscan without warnings and score 90+ in Lighthouse. Here's how it works.

Problems We Solve

N+1 queries via {{#get}}. A common mistake is fetching data for each post with a separate request. The solution is to use eager loading via the include parameter in the main query or extract data into partials with memoization.

Lack of responsive images. Without img_url with size and srcset, the theme loads full originals — 3000px on mobile. This dramatically increases LCP and CLS. The correct approach is to define image_sizes in package.json and build srcset for each image.

Neglecting custom settings. Many developers hardcode styles and functionality that the editor could change via Admin. We use @custom in the config — this gives the editor control without redeployment.

How Handlebars Affects Ghost Performance

Handlebars allows creating safe templates without the RCE risk typical of PHP engines. Ghost version 5.0+ stably supports all helpers for building complex interfaces, from pagination to Members. Compared to Nunjucks, Handlebars is the native language that gives maximum control with minimal room for error. It's 2x faster than PHP template engines in WordPress under equal load, due to no code execution overhead.

How We Do It: Media Blog Optimization Case

Client — a media with 5000+ posts, theme on old Bootstrap, LCP 4.2s, INP 300ms. Goals: preserve design, improve Core Web Vitals, add Members for premium content.

Stack: Ghost 5.5, Handlebars, Node.js 18, Nginx, Cloudflare. We completely rewrote the template structure:

  • Replaced nested {{#get}} calls with a single query using include=tags,authors.
  • Configured image_sizes with four variants and added sizes attribute.
  • Implemented lazy loading for below‑fold images.
  • Used @custom for theme colour scheme and header type selection.

Result: LCP — 1.8s, CLS — 0.05, INP — 100ms. The site passed gscan without issues.

Process of Work

  1. Analytics: audit the current theme, measure metrics, identify bottlenecks.
  2. Design: create partial structure layout, custom settings schema, prototype Members flows.
  3. Implementation: write all hbs templates, styling, package.json configuration, integrate with Ghost API.
  4. Testing: gscan validation, device checks, Lighthouse profiling.
  5. Deployment: upload via Admin or CI/CD, configure caching on Nginx and Cloudflare, monitor metrics.

Approximate Timelines

Theme Type Time
Minimal working theme 2–3 days
Full theme (index, post, tag, author) 5–8 days
With Members and custom settings 8–12 days
With Newsletter templates +1–2 days

Pricing is individual. We estimate the project within one day.

Typical Mistakes in Ghost Themes

Mistake Solution
Missing error.hbs Create the mandatory file
Using {{#each}} without post_class Always apply {{post_class}}
Ignoring card_assets Include in package.json
Hardcoded markup instead of responsive Use img_url with srcset
Forgotten @custom settings Export all parameters to package.json

What is Included in the Work

  • Theme source code with comments
  • Documentation for custom settings
  • Members and Newsletter setup
  • gscan validation with zero warnings
  • 30 days of free support after delivery

How to Implement Members Restrictions in a Custom Theme?

Add an access check block in post.hbs. Ghost provides the {{#if access}} context for paid content and {{#unless @member}} for guests. Example:

{{#post}} {{#if access}} <div class="premium-content">{{content}}</div> {{else}} <div class="paywall"> <h3>This article is for subscribers</h3> {{#unless @member}} <a href="#/portal/signup">Subscribe</a> {{/unless}} </div> {{/if}} {{/post}} 

To display plans, use {{#get "tiers"}} and output name and price.

Template Examples

Core Helpers on index.hbs

{{! index.hbs — posts list}} {{#foreach posts}} <article class="post-card {{post_class}}"> {{#if feature_image}} <figure> <img srcset="{{img_url feature_image size="s"}} 300w, {{img_url feature_image size="m"}} 600w, {{img_url feature_image size="l"}} 1000w" sizes="(max-width: 768px) 100vw, 50vw" src="{{img_url feature_image size="m"}}" alt="{{title}} — custom Ghost theme"> </figure> {{/if}} <div class="post-card-content"> <header> {{#primary_tag}} <a href="{{url}}" class="post-tag">{{name}}</a> {{/primary_tag}} <h2><a href="{{url}}">{{title}}</a></h2> </header> {{#if excerpt}} <p>{{excerpt words="30"}}</p> {{/if}} <footer> {{#primary_author}} <img src="{{img_url profile_image size="xs"}}" alt="{{name}}"> <a href="{{url}}">{{name}}</a> {{/primary_author}} <time datetime="{{date format="YYYY-MM-DD"}}"> {{date format="DD MMM YYYY"}} </time> {{#if @custom.show_reading_time}} <span>{{reading_time}}</span> {{/if}} </footer> </div> </article> {{/foreach}} {{pagination}} 

Dynamic Data via {{#get}}

{{! Recent posts by tag}} {{#get "posts" limit="5" filter="tag:javascript" order="published_at desc"}} {{#foreach posts}} <a href="{{url}}">{{title}}</a> {{/foreach}} {{/get}} 

Mandatory Theme Files

my-theme/ ├── package.json ├── index.hbs ├── post.hbs ├── page.hbs ├── error.hbs ├── tag.hbs (optional) ├── author.hbs (optional) ├── partials/ │ ├── header.hbs │ ├── footer.hbs │ └── post-card.hbs └── assets/ ├── css/screen.css └── js/main.js 
{ "name": "my-theme", "description": "Custom Ghost theme", "version": "1.0.0", "engines": { "ghost": ">=5.0.0", "ghost-api": "v5" }, "license": "MIT", "config": { "posts_per_page": 12, "image_sizes": { "xs": { "width": 300 }, "s": { "width": 600 }, "m": { "width": 1200 }, "l": { "width": 2000 } }, "card_assets": true, "custom": { "header_style": { "type": "select", "options": ["Center", "Left", "Right"], "default": "Center" }, "show_reading_time": { "type": "boolean", "default": true } } } } 

Contact us for a project estimate. Order custom Ghost theme development — we guarantee quality code that passes gscan with no errors. Get a consultation for your project — we'll assess complexity and timelines within one day.