Making {{ghost_head}} better

Know how I'm always griping about ghost_head being a monolith? I did something about it!

Making {{ghost_head}} better
đź”®
The PR is just got merged and should be in 5.99.

Introducing new options for {{ghost_head}}!

Those of you who've been reading for a while may have noticed that I'm bothered by {{ghost_head}}. I mean, it does a ton of awesome and useful stuff, but the fact that it's one beast of a monolith means that it's also a barrier to being able to do other interesting and useful things.

Life without {{ghost_head}}
If you want to customize more fully, you might want to replace {{ghost_head}} with your own code. Here we break it down.

I did one client job that needed {{ghost_head}} replaced, and wished for the ability to adjust it without needing to completely omit it.

A year passed, and then, after some consultation with the Ghost team, I wrote it!

(Actually, I wrote it twice, because the first time, what I thought we'd agreed on didn't really work very well, and then I got carried and built a bunch of added flags that weren't actually wanted. So yeah. Coding.)

I'm super excited about this, because it's going to give anyone with the ability to load a custom theme super powers. You can load a custom version of Portal. You can replace sodo-search with a custom-built search that uses Algolia as the back end. You can not load the card assets on the front page if you don't have full post content on the front page. You can load customized metadata and schema.

All the things, and all available to anyone who can upload a custom theme.

Mini-docs:

Key
What it excludes
Additional Info/Why you should or shouldn't.
(empty array)
No changes to current behavior

search
The built-in sodo-search script
Includes adding the click event listener on buttons, generating the search index, and the UI.
portal
The portal script
Handles sign-in and sign-up, payments, tips, memberships, etc, and all the portal data-attributes.
announcement
The announcement bar javascript
If you'd like to use the announcement bar admin settings but not have it mess up your CLS metric, this is for you.
metadata
Skips HTML tags for meta description, favicon, canonical url, robots, referrer
Important for SEO
schema
The LD+JSON schema
Important for SEO
card_assets
Loads cards.min.css and .js
Needed on any page with a post body, unless your theme replaces them all. Assets can also be selectively loaded with the card_assets override
comment_counts
Loads the comment_counts helper
Needed if the page is using {{comments}} or data-ghost-comment-count attribute
social_data
Produces the og: and twitter: attributes for social media sharing and previews
Required for good social media cards
cta_styles
The call to action (CTA) styles written out inline in the head of each page
Used for member signup and CTA cards - may be overwritten by your theme already

Examples

Example: Don't load card_assets on pages that don't display a post body. (Theme-dependent.)

{{#is "home, tag, author"}}
   {{ghost_head exclude="card_assets"}}
{{else}}
   {{ghost_head}}
{{/if}}


Example: Give only paying members access to your custom search application (included from "custom-algolia-integration"). Write out a different schema for posts with the #recipe tag.

{{#if @member.paid}}
   {{#has tag="#recipe}}
      {{ghost_head exclude="schema,search"}}
      {{> custom-schema-partial}}
   {{else}}
      {{ghost_head exclude="search"}}
   {{/has}}
   {{> custom-algolia-integration}}
{{else}}
   {{#has tag="#recipe"}}
      {{ghost_head exclude="schema"}}
      {{> custom-schema-partial}}
   {{else}}
       {{ghost_head}}
   {{/has}}
{{/if}}

Example: Load a different version of the portal.

{{!-- load a different version of portal & search depending on the page the user is on --}}

{{#has tag="#de"}} 
    {{ghost_head exclude="portal,search"}} 
     <script defer src="https://cdn.jsdelivr.net/ghost/portal@2/umd/portal.min.js" 
        data-i18n="true" 
        data-ghost="{{@site.url}}" 
        data-key="{{content_api_key}}" data-api="{{content_api_url}}" 
        data-locale="de" crossorigin="anonymous">
     </script>
     <script defer src="https://cdn.jsdelivr.net/ghost/sodo-search/umd/sodo-search.min.js" 
        data-key="{{content_api_key}}" data-styles="https://cdn.jsdelivr.net/ghost/sodo-search/umd/main.css" 
        data-sodo-search="{{@site.url}}" data-locale="de" crossorigin="anonymous">
     </script>
{{else}} 
     {{ghost_head}} 
{{/has}}


Example: Load comment counts only on pages that display them. (Will vary with theme.)

{{#is "page, post"}}
    {{ghost_head exclude="comment_counts"}}
{{else}}
    {{ghost_head}}
{{/is}}

Example: Load CTA assets only in situations that need them.

{{#is "post, page"}}
    {{#if @member.paid}}
        {{ghost_head excludes="cta_styles"}}
    {{else}}
        {{ghost_head}}
    {{/if}}
{{else}}
    {{ghost_head excludes="cta_styles"}}
{{/is}}

Example: Build the CTA styles (or their replacements) into the theme's styles.min.css so that they get loaded once and then cached by the browser.

{{ghost_head exclude="cta_styles"}}

Example: Pages about your book need og:type of book instead of article

{{#is "page"}}
    {{#has tag="#book"}}
        {{ghost_head exclude="social_data"}}
        <meta property="og:type" content="book">
        <meta property= ...>
        {{!-- note: the theme creator will want to replace all metadata, not just this one item. --}}
    {{else}}
        {{ghost_head}}
    {{/has}}
{{/is}}

Note: Replacing social_data or schema is a significant task. Theme creators may want to look at how {{ghost_head}} generates this content.

Technical bits, for those curious:

🎨 Added an “exclude” attribute to {{ghost_head}} by cathysarisky · Pull Request #21229 · TryGhost/Ghost
no ref {{ghost_head}} is huge, and some power-users and theme creators want the ability to customize what it contains. This PR makes it easier for a theme to write custom schema, or to load a cust…

How hard was it to do? Here's my lines added/removed:

OK, it wasn't actually quite as bad as it looks, since a lot of it is snapshots and new tests. It took longer to write the tests than the original feature, for sure. (Testing revealed a bug that would have been a problem for Ghost Pro users, so it was all worth it. I think...)

I'm super excited to see what you build with this. Drop me a comment below, especially if you're now loading a custom version of Portal, in pink!


Would you like to sponsor more work?