Ghost ships with a clean, simple AMP template which works with just about any site, but wouldn't it be nice if you could customise it to make it perfectly match your theme and your brand? Well you can! Read on:

How does AMP work in Ghost?

AMP doesn't need to be enabled in Ghost, it's completely automatic.

As soon as you add /amp to the url of any post URL (e. g., you'll see this template file being rendered.

All blog posts also get a new canonical link, which refers to the AMP version of the post:

<link rel="amphtml" href="" />

This is a necessary link. Without it, your AMP post will not be served from the Google AMP Cache)

When Ghost detects an amp.hbs template in the active theme directory, it will render this template instead. So, that's the place, where we need to add the customized template. It should be embedded in the file structure like this:

├── /assets
|   └── /css
|       ├── screen.css
|   ├── /fonts
|   ├── /images
|   ├── /js
├── default.hbs 
├── index.hbs [required]
└── post.hbs [required]
└── amp.hbs [optional]
└── package.json [required]

Be aware of the AMP restrictions

Before you start developing your AMP template, you need to know a few things more about the restrictions which Google has enforced in AMP to maintain optimal performance:

⚡ No client side scripts

AMP pages can't include any client side scripts, except under special circumstances. Any use of jQuery or JavaScript will lead to an invalid AMP template!

⚡ Keep CSS inside <style amp-custom>

CSS can only live in the <style amp-custom></style> tag inside the <head> section. You can use class and id in your HTML, but you can't use inline CSS. The CSS is also size limited to 50 kilobytes. Additionally, some CSS styles are disallowed for performance.

⚡ Prohibited HTML

AMP doesn't uses it's own AMP-HTML markup language. This means that some HTML tags are prohibited while others need to be transformed. Ghost automatically transforms HTML in your {{content}} (e. g. <img> becomes <amp-img>) and strips out disallowed tags, like embed or frame. You can find a full list of banned HTML tags here.

⚡ Required AMP markup

AMP has a few pieces of required markup which must be used.

⚡ Links to media need https

If you want to load stuff externally, you'll need to use https protocol. Ghost does this automatically for any links in your post {{content}}.

⚡ Media needs width & height

All kind of media need width and height attributes. Again, anything being rendered within post {{content}} will be handled automatically. Here's an example of how you'd implement an image outside of the main content:

  <amp-img src={{image absolute="true"}} width="600" height="400" layout="responsive"></amp-img>

or the author image:

  	<amp-img src={{image absolute="true"}} width="50" height="50"></amp-img>

⚡ Validate your template on-the-fly

AMP provides two ways to validate your template. The first is to add #development=1 to the URL and check for any validation errors in the console. The second is to copy your HTML into their online validator, but keep in mind that this will not work with Ghost handlebars helpers.

Create your amp.hbs template

The easiest way to get started is to copy the default amp.hbs template from core/server/apps/amp/lib/views/amp.hbs into your template directory, and start making changes. Just open a blog post in your browser and add /amp/#development=1 to the URL. This will not only render your AMP template, but also open the AMP validator.

The <head>

<!DOCTYPE html>
<html ⚡>
    <meta charset="utf-8">
    <meta name="description" content="{{meta_description}}" />
    <meta name="HandheldFriendly" content="True" /> //required
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> //required
    <link rel="shortcut icon" href="{{asset "favicon.ico"}}">
    <link rel="stylesheet" type="text/css" href=",700,700italic,300italic|Open+Sans:700,600,400" />
    <style amp-custom>
    <style amp-boilerplate>...</style><noscript><style amp-boilerplate>...</style></noscript>

    <script async src=""></script>


If you're a theme developer, this is probably all very familiar. Here are the highlights:

<html ⚡>

This makes it AMP! You can use <html amp> alternatively. But ⚡ is more awesome.

<meta charset="utf-8">

This is required by AMP

<meta name="HandheldFriendly" content="True" />

This is required by AMP

<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">

This is required by AMP


This helper will output the mandatory structured data and the JSON+LD data, just like {{ghost_head}} does. It also renders the necessary link to the regular post, which is (wait for it) required by AMP. 💃🏻

Code injection content won't be rendered here, as this may contain additional <style> and <script> tags.

<link rel="stylesheet" type="text/css" href="" />

AMP does allow the use of custom font faces.

<style amp-custom>

All your CSS goes here. The default AMP template comes with a similar structure to the Casper theme. You can work with CSS classes just as you're used to. Just keep in mind that there are some CSS restrictions, as mentioned above.

<style amp-boilerplate>

This is required by AMP, and essentially outputs a few default AMP UI styles for animations/etc which are mandatory. You can find the current version here.

<script async src=""></script>

This will load the AMP JavaScript library and is required by AMP.


This is a special helper which scans your {{content}} for any extended components, such as .gif files, iframe-tags and audio tags. If it finds them, it'll include scripts to make them work good. Yes, work good. That's what we're going with.

The <body>

Build the structure for the content:

<body class="amp-template">
    <header class="main-header">
        <nav class="blog-title">
            <a href="{{@blog.url}}">{{@blog.title}}</a>
    <main class="content" role="main">
        <article class="post">
            <header class="post-header">
                <h1 class="post-title">{{title}}</h1>
                <section class="post-meta">
                    <p class="author">by <a href="{{url}}">{{name}}</a></p>
                    <time class="post-date" datetime="{{date format="YYYY-MM-DD"}}">{{date format="YYYY-MM-DD"}}</time>
            {{#if image}}
            <figure class="post-image">
                <amp-img src="{{image absolute="true"}}" width="600" height="400" layout="responsive"></amp-img>
            <section class="post-content">
    <footer class="site-footer clearfix">
        <section class="copyright"><a href="{{@blog.url}}">{{@blog.title}}</a> &copy; {{date format="YYYY"}}</section>
        <section class="poweredby">Proudly published with <a href="">Ghost</a></section>

This is where you can go wild! Again, I will only explain the new and/or important stuff:

{{image}} & <amp-img>

The default template uses the {{#if}} helper to check for a post image. But you'll notice that it needs to be wrapped in <amp-img> and also provide the mandatory width and height attributes.


A special version of the regular {{content}} helper which transforms HTML tags into AMP HTML where appropriate.

Next Steps

That's all there is to it! You now know everything there is to know about creating a custom AMP template for your Ghost publication. Aw yeah.

Thoughts? Questions? Drop us a line below!