Context
While on a journey creating a theme for a Ghost website, you may come across cases where you're looking for a specific Handlebars helper to help you design the theme you want.
Unfortunately, after looking at all helpers that are available in the documentation, or directly in the source code, you realize the fact: it is currently not possible to do what you want. 😥
One such case I recently encountered was quite simple: I want to get the Twitter username without its @.

If you look at how to get the Twitter account set in Ghost backend, you might quickly find the {{twitter_url}} helper. Unfortunately, it returns the absolute URL to the Twitter account, like https://twitter.com/BenRancourt. 😒
We can also get the Twitter account with the @site helper, but it still returns the Twitter username with an @ sign... 😓
{{@site.twitter}} === @BenRancourt
{{twitter_url}} === https://twitter.com/BenRancourtBenRancourt?But wait, why do I need to get this data in this exact format? 🤔 Simply to create a share button to Twitter in my posts.

Twitter provides us a Web Intent URL to help site visitors compose and post a Tweet. A configured URL might look something like below.
https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.benjaminrancourt.ca%2Fshould-i-unjam-my-ghost-blog%2F&text=Wondering%20if%20you%20should%20Jamstack%20your%20Ghost%20site%3F%20You%20might%20want%20to%20read%20this%20article%20because%20I%20was%20wondering%20otherwise.%20%F0%9F%99%8A&via=BenRancourtOne available query parameter is via, which associates a Twitter username with the Tweet. Unfortunately, it doesn't allow the @ sign... 😑
Two solutions quickly emerged:
- Hardcode my username in the link
- Add
&via=BenRancourtdynamically with JavaScript.
Since I want to explore Ghost more this year, I decided that I could also try creating my own Handlebars helper in Ghost. 😋
Turns out it's relatively easy to create one if you have access to your server. Let's see! 😜
Tutorial
If you look at Ghost's source code in GitHub, you might quickly find that all helpers available to use in a theme are in the same folder: core/frontend/helpers. You may want to base your new helper on a existing one, to familiarise yourself with the necessary syntax.
In Ghost, there are two types of helpers: block and string-based. Let's see a quick definition to help you know which helpers you might want to take a look at.
Definitions
String helpers
String helpers are usually the easiest helpers to understand when looking at their source code. As their name suggests, they produce text. Their syntax looks like {{name_of_the_helper}}.
Here is a list of some string helpers you may have used:
Block helpers
Block helpers, which are the second type of helpers, are generally a bit more complex than string helpers. The syntax for using them is also different:
{{#name_of_the_helper}}
CONTENT
{{/name_of_the_helper}}They also produce text, usually based on the CONTENT, but with a lot more code. 😜
Here is another list of some block helpers currently available in Ghost:
Enough with the definitions, let's see how to implement one. 😂
Implementation
Since we want to get the Twitter username, let's base our work on the {{twitter_url}} helper.
// # Twitter URL Helper
// Usage: `{{twitter_url}}` or `{{twitter_url author.twitter}}`
//
// Output a url for a twitter username
const {socialUrls} = require('../services/proxy');
const {localUtils} = require('../services/handlebars');
// We use the name twitter_url to match the helper for consistency:
module.exports = function twitter_url(username, options) { // eslint-disable-line camelcase
if (!options) {
options = username;
username = localUtils.findKey('twitter', this, options.data.site);
}
if (username) {
return socialUrls.twitter(username);
}
return null;
};{{twitter_url}} helper.Create the file of your helper
Since the necessary changes are relatively easy to understand, let me show the code I put in core/frontend/helpers/twitter_username.js.
// # Twitter username Helper
// Usage: `{{twitter_username}}` or `{{twitter_username author.twitter}}`
//
// Output the Twitter username, without @
const { localUtils } = require('../services/rendering');
module.exports = function twitter_username(username, options) { // eslint-disable-line camelcase
if (!options) {
options = username;
username = localUtils.findKey('twitter', this, options.data.site);
}
return username?.substring(1) || "";
};
{{twitter_username}} helper.If you restart your Ghost instance, you should now be able to use it in your theme. 😋

Unfortunately, if you tried to upload your theme in Ghost administration, it should fail because it doesn't recognize our new helper. 😫
Inform gscan of the existence of your helper
There is actually a scanner, named gscan, which checks if your theme is valid and doesn't use unknown helpers. We will therefore have to inform this utility, included with Ghost, that we have created and implemented a new Handlebars helper.
If you open the gscan source code, you might notice in lib/specs/v4.js file that all the helpers introduced in Ghost v4 are listed there.
const previousKnownHelpers = previousSpec.knownHelpers;
// Assign new or overwrite existing knownHelpers, templates, or rules here:
let knownHelpers = ['match'];
knownHelpers = _.union(previousKnownHelpers, knownHelpers);Just add the name of your helper here and it will now be recognized. 😀
let knownHelpers = ['match', 'twitter_username'];You are now fully ready to use your new helper! 😎
To create or edit a helper, you must have access to the server where your Ghost instance is installed.
Additionally, your theme will become truly paired with your forked Ghost instance. You won't be able to use it on a unmodified version of Ghost, such as Ghost(Pro).
If you can't create the helper you want or don't want the burden of maintaining it, ask the community on Ghost forum. You can also try to convince the Ghost team that we need a new Handlebars helper. 😉
Conclusion
I hope this tutorial helped you better understand the Handlebars helpers available in Ghost and that you were able to create a new one! 🤗
Ad if you think the community would benefit from your helper, feel free to submit a Pull Request on GitHub! 😏






