Using AMP with Jekyll

Written on July 18, 2020 , Last updated on: July 18, 2020

I decided to make AMP work with Jekyll after I was heartbroken by the mess called Facebook instant article. AMP is easier, but if you want to use it with jekyll, there are two options: either use a theme that allows posting AMP articles, or using a plugin that makes AMP versions of your existing posts. The second option is obsolete, and I did not want to change my theme. Moreover, there is also this issue of AMP not supporting a lot of things like syntax highlighting and what not, and I did not want to make all of my posts AMP based.

So I had to make my own option: create a manual-ish approach that automates everything as much as possible with me having a fine degree of control on what gets to be converted.

1. Creating Inline CSS

AMP only allows inline CSS. That is, no link rel based CSS loading. Jekyll themes mostly uses SCSS (SASS CSS) based style. Therefore, I created a copy of the existing style.scss file in the _includes folder, named amp_style.scss. I had to change the existing imports, so something like this:

- @import "reset";
- @import "variables";
+ @import "../_sass/reset";
+ @import "../_sass/variables";

In essence, the amp_style.scss file will be importing necessary scss files from the _sass folder. However, to use it, we still need to include it in our AMP article. So we do that through the help of a new layout file, namely amp.html in _layouts folder. It is mostly going to be the same as the default.html layout, except we are going to remove scripts and imported stylesheet file, since AMP does not allow either of those.

You may not have realized this, but a serious advantage due to this step is that you can now use all your style in AMP as long as you are putting them in the inline format.

2. Making a AMP specific Layout File

The AMP specific layout file will include all the necessary AMP related tags, scripts and necessary imports. However, instead of using <link rel="">, we will use something else:

{% capture styles %}
    {% include amp_style.scss %}
{% endcapture %}
<style amp-custom> 
    {{ styles | scssify }}

What’s happening here is that it is reading the amp_style.scss file with all its imports and what not and storing it in the styles variable. Then, we are using the scssify on that variable to write the contents inside a <style amp-custom> tag.

Gotcha: You might have to change sass style to compressed in the _config.yml file

In the layout file, you need to make sure that:

  • It does not have anything related to javascript unless acceptable in AMP specification.
  • It does not have anything related to importing stylesheets.

A sample layout file is provided here for your convenience. Click on it to expand:

Sample _layouts/amp.html
<!doctype html>
<html amp lang="en">
    {% include meta.html %}
    <link rel="canonical" href="{{ site.url}}{{site.baseurl}}{{ page.url | replace: '/amp', '' }}">
    <title>{% if page.title %}{{ page.title }} – {% endif %}{{ }}</title>
    {% capture styles %}
    {% include amp_style.scss %}
    {% endcapture %}
    <style amp-custom> 
      {{ styles | scssify }}

<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>

    <link rel="alternate" type="application/rss+xml" title="{{ }} - {{ site.description }}" href="{{ site.baseurl }}/feed.xml" />
    {% include favicons.html %}
    <script async src=""></script>

    <div class="wrapper-masthead">
      <div class="container">
        <header class="masthead clearfix">
          <a href="{{ site.baseurl }}/" class="site-avatar">
            <!-- <img src="/{{ site.avatar }}" alt="{{ }}"/> -->
            <amp-img width="480" height="480" layout="responsive" src="/images/resized/480/portrait.webp"></amp-img>

          <div class="site-info">
            <h1 class="site-name"><a href="{{ site.baseurl }}/">{{ }}</a></h1>
            <p class="site-description">{{ site.description }}</p>

            <a href="/">Home</a>
            <a href="{{ site.baseurl }}/">Blog</a>
            <a href="{{ site.baseurl }}/archive/">Archive</a>

    <div id="main" role="main" class="container">
      <article class="post">
        <h1>{{ page.title }}</h1>
        <div class="date">
          Written on {{ | date: "%B %e, %Y" }}
          {% if page.last_modified_at %}
          , Last updated on: {{ | date: "%B %e, %Y" }}
          {% endif %}
        <div class="entry">
          {{ content }}
    <div class="wrapper-footer">
      <div class="container">
        <footer class="footer">
          {% include svg-icons.html %}

The gist version is provided for convenience.

Please remember that this is NOT a template file you can use as it is. You may have to change it according to your own needs.

3. Establishing Relation from AMP to non-AMP page

AMP requires you to relate the canonical version of the non-AMP page in the AMP version of the page.

I am assuming that the URL of the page and the AMP version of the page will follow similar structure. So, something like this:

non-AMP URL:
AMP URL    :

I will also discuss how I made sure that both URLs are created in similar format at later part of this article.

So, this is what I did in the amp.html layout file.

<link rel="canonical" href="{{ site.url}}{{site.baseurl}}{{ page.url | replace: '/amp', '' }}">

This snippet will get the full URL of the AMP page, and then will replace the /amp with nothing so that it is converted to the non-AMP URL when the HTML will be created. Thus, it will create link from the AMP page to the non-AMP page.

4. Establishing relation From non-AMP to AMP page

Similarly, we need to notify that the non-AMP version of the page also has a AMP version. We achieve this by introducing the following snipper in the default layout.

{% if page.amp %}
    <link rel="amphtml" href="{{site.url}}{{site.baseurl}}/amp{{ page.url }}">
{% endif %}

Again, this is fairly similar to the previous step, except now we are making sure that the hyper reference includes an additional /amp. This goes without saying, but now in your non-AMP version of the page, you will need an amp: true in front matter if you want to establish this relation. Otherwise, by default this relation will not be expressed in the produced HTML file.

5. Making a Similar URL structure for AMP pages

I wanted to make this as convenient as possible when making AMP version of pages. Ignoring all the discussions related to experiments I did, this is what I ended up doing.

5.1. Create an _amp folder

In this folder, I put the same markdown file I create for _posts. So, for example, this post will have two markdown files.

AMP    : _amp/
non-AMP: _posts/

There are two notable changes in the front matter for the AMP version:

layout: amp
sitemap: false

Setting the layout to amp is obvious. We do not need to have this in sitemap because of the relation between AMP and non-AMP version of page.

5.2. Configuring the URL structure for the non-AMP page

This part is the most important piece of the puzzle:

permalink: /:year/:month/:day/:title/
    output: true
    permalink: /amp/:year/:month/:day/:title/

The first line is the permalink structure for your usual, non-AMP posts. The collections portion is doing several things:

  • since the _amp folder starts with an _, it is by default ignored when building. We are configuring it through a collection so that it will not be ignored when building.
  • The collections.amp.permalink adds the /amp/ sub string, and then follows the same structure from the permalink. This ensures that both the AMP and non-AMP version of the page will create similar, canonical URLs.

6. Writing Once, Putting everywhere

Instead of having the same content in both AMP and non-AMP version of the page, this is what I do.

  • create a posts folder inside _includes
  • create a .md file inside the _includes/posts/ that only contains the contents
  • include it in both the amp and non-amp version of the page, like so:
{% include posts/ %}

And you are done! 😄

7. Bonus: If you want excerpt

You probably will notice that the above approach of writing once, put everywhere may end up not respecting the excerpt tag. The solution is to use the excerpt in the front matter, like so:

excerpt: "I decided to make AMP work with Jekyll after I was heartbroken by the mess called Facebook instant article. ... etc."

Aaand, that’s it!

ps: I still have not figured out a way to add amp-img. But I barely use them anyway!

8. Sample Repository/Code/Website

A Sample Source code repository built using the Jekyll Now with AMP support is available here:

This post is created using this same tutorial. Still, if you want to see sample repository in working, check this:

First, click on "Comments" below to view/post comments.
To comment as guest, click on the field "Name". The option to do so will become visible.
লগইন ছাড়াই কমেন্ট করতে নাম এ ক্লিক করুন, দেখবেন তার নিচেই আছে অতিথি হিসাবে কমেন্ট করার অপশন।