Accelerated Mobile Page (AMP) wasn’t something I spent much time thinking about until recently when I started building blogs as static websites. Because I’m building my sites statically, I have to look at each aspect of my site to craft it in the best way possible. I also don’t want to spend time working on versions of my pages that may end up being a waste of time. To that end, I did a lot of research and trial and error with AMP pages before I implemented them on my own site.
AMP pages are an essential part of your website experience. Implementing AMP on your pages will create a better mobile experience for your users, and it’s the only way of getting featured in the top stories carousel on mobile devices. AMP pages are your way of optimizing the experience for your mobile users. Since AMP pages are cached versions of your page, running AMP pages along with the desktop version of your pages makes perfect sense.
If you want to skip ahead to the technical part where I show you how I implement AMP on my site or check out just the code.
What AMP Is and Why You Need it
Although AMP seems to be associated with Google, it’s actually an open source project with support from other major tech companies like Microsoft. AMP is designed to optimize the web browsing experience for mobile users. It is intended to bridge the gap between desktop versions of web pages and mobile users by offering an optimized experience for mobile users.
Mobile users have historically been neglected by a large number of sites and web developers. Now that most sessions on websites come from mobile devices, it’s more important than ever for developers to provide the best experience possible for their mobile users.
Everything in AMP is designed to be optimized for both speed and ease of development. Priority is placed on user experience, so faster speed at the expense of user experience is not emphasized.
To help achieve a better user experience, the AMP Project is also trying to make website development easier. It’s not designed for the best case scenario or for a better browser that may one day be available. Instead, it’s focused on developing in an extensible manner to take advantage of new technologies as they emerge. In fact, if you look at the AMP Project Roadmap, you can see everything that the AMP team is working on for the future of AMP.
Features from Using AMP
If your site isn’t using AMP, there are several benefits that your site will lose out on. Google’s top stories carousel on mobile devices requires that your site implements AMP to be eligible. Although there may be plans to integrate sites that meet Google’s performance requirements into the carousel, there is no definitive plan or timeline for doing so.
Also, the product featured search results have a carousel of their own that incentivizes eCommerce websites to adopt AMP. Although some publishers have complained that AMP gives too much control of their content to Google, AMP pages are trying to be publisher friendly. Just looking at a couple of examples shows the AMP Project’s drive to make it a publisher-friendly technology. The top stories carousel that seems built for news companies and content publishers and the release of code designed to optimize the ad experience on AMP pages are just a couple of the examples readily available.
Criticisms of AMP
Though AMP seems to offer features to encourage development and provide a better user experience, it’s not without its criticisms. Depending on which critic you’re talking to, AMP is either a mild annoyance to a technology that will end the web as we know it.
I’m not much one for the doom and gloom situations that people like to attribute to new technologies. Too often they are merely reacting to change itself, not to the latest technologies.
The most relevant criticism that I’ve seen is from publishers that claim that too much control is being given over to Google and other search engines. Content publishers often rely on multiple page sessions to drive income through affiliate marketing or advertisements. From what I’ve seen, a lot of that criticism should be directed towards the implementation of AMP rather than the technology itself.
AMP allows you to create your own custom styling so you can make your page look merely identical to your desktop counterpart. Since much of the time users spend waiting for a web page is spent waiting for tertiary content - as opposed to the main content that they’re there to consume - it makes sense that AMP is trying to limit the number of resource requests initiated by web pages.
Maybe I can clear up some of the confusion by going over how AMP works.
How AMP Works
AMP works by determining what content is displayed on your page and how it is displayed ahead of time. It then stores cached versions of your AMP pages on CDNs distributed throughout the world.
Remember that AMP is designed to give an optimized mobile experience, part of which is optimizing how pages are delivered to users’ browsers. There are a few fundamental problems that AMP is trying to resolve, too many requests and delays in sending the content caused by the calculations of the backend.
To solve the issue of too many HTTP requests, AMP doesn’t allow you to load custom Javascript or external CSS files. It may seem like a rather strict rule, but when you consider how each type of file is processed, it makes sense.
CSS Files
CSS files are not usually extensive, and many developers have questioned why the AMP team does not allow external CSS files. There hasn’t been an official announcement from the AMP Project team explaining their decision. If you think about how browsers load and display data, they won’t start displaying content until external CSS files are downloaded. So, it follows that limiting external CSS files will help speed up the display of the site.
Also, AMP relies on being able to load and display pages immediately. It uses specific measurements on elements of your page, so it knows how to lay out the page ahead of time. If you use external CSS files, it can’t make the calculations ahead of time.
Javascript Files
Javascript has a myriad of features that can be implemented using the language. The ability to dynamically load content, change the styling of the page and even alter the layout of the page. Since AMP needs to know what content you have and how your page is laid out ahead of time, allowing you to include custom Javascript files interferes with the goals of the project.
Additionally, Javascript files can be extremely large. They may not be as large as multimedia files, but having extra - often unnecessary for mobile - doesn’t make sense. For instance, if you have Javascript to add mouseover events to your page, these rarely occur on mobile devices. Controlling which Javascript you can use helps ensure AMP pages have the best user experience possible.
One caveat to this is that you can use Javascript if you include it in a custom iframe. If your Javascript is loaded in an iframe, it can’t block the loading or construction of the document object model (DOM).
The Dos and Don’ts of AMP HTML
Most of the HTML that you already have on your web pages you will be able to use for your AMP pages. The exceptions are for multimedia objects. For images, videos, and iframes, there are AMP-specific versions that you must use.
Sizes
All of your content must state its size explicitly in your HTML. In your <amp-img>
and <amp-video>
tags, you’ll have to declare the height and width of your content inside as attributes of the elements.
Javascript
We’ve already established that you can’t use custom Javascript or external CSS files. You can rely on the AMP Javascript for most of what you would use Javascript for. It includes support for carousels, lightboxes, and sidebars. If you absolutely must use custom Javascript on your site, put it in an iframe.
Inline CSS Only
All of your CSS has to be included inside a style element with the syntax <style amp-custom>
. Using inline CSS helps speed up page rendering. AMP also limits custom CSS to 50KB, which is about 10,000 lines. This should be enough to style your site, but be sure to practice good CSS habits to avoid bloating your CSS unnecessarily.
Boilerplate
The AMP boilerplate is a specific code block that you must include inside the header of your document. You are not allowed to alter the boilerplate content, except for replacing the spaces with other forms of whitespace. I don’t know why you would need to modify the display of the boilerplate code, but if you wanted to, just know that you won’t be able to.
Fonts
Because of the way that AMP limits CSS (inline only) and Javascript (all use the async tag), browsers are able to start downloading fonts when your page loads immediately. AMP requires that your fonts be optimized for the web. If you’re using Google Fonts, then you don’t have anything to worry about. The fonts available from Google already optimized for the internet, but be careful not to pull too many fonts for your site.
Implementing AMP
If you’re custom developing your site, it can be easy to add a bit of logic to your page to include the correct AMP version of the tags. This also makes it easier as you have a single touch-point for all of your content, and if you need to make changes, then you just have to make them once.
If I didn’t use logic to split my article template into the primary page and AMP page versions, it would needlessly complicate my file management process. If I had a separate article template for the AMP version of my article page, then I’d have to have a different AMP version of my layout template, of my navigation template, of my footer template…you get the point.
If you haven’t read any of my articles in the past, let me explain how my development environment is set up. I use python with the Flask microframework. I use Flask-Flatpages to generate my content pages from markdown files, and I use Frozen-Flask to produce the static version of my site. I use Brackets to write my code, and I use the Flask web host to test my site.
You can still use a similar approach to creating AMP versions of your content pages, but you’ll need to change the plan to suit your language or framework of choice.
Now that that’s out of the way let’s get started.
Setup the Application FIle
The application file is what controls everything in your file. Consider it the brain of your site. You’ll need to create a route to point to the AMP version of your page. Based on Google’s recommendation, you could either put /amp at the end of your URL, or put it at the beginning after the base URL.
The way I like to set up my route is to put /amp at the end of my URL. The reason I set mine up this way is that I like that I can easily switch between versions of my page by just tacking on /amp at the end (although I admit inserting /amp into the middle wouldn’t be much more difficult.
The main reason I like to put it at the end is because how my files are organized when using Frozen-Flask. If I were to put /amp in the middle of my page, my content structure would look like this:
site base folder
amp
category
amp page URL
category
main page url
If I’m having an issue with one of my pages, I would need to browse through the directory for both versions of the page to check them. By putting /amp at the end of my URL, my content structure looks like this:
site base folder
category
main page url
main page folder
amp page url
For me, that makes it easier to troubleshoot or validate my pages. It also just makes more sense to me to organize my site content that way.
Here’s the route code that I use to set up the route for my page. Just as a note, other than the route and including ampPage
in the render_template
function, it’s the same route as what I use for regular articles.
@app.route('/<category>/<name>/amp.html')
def amp_article(name, category):
article = flatpages.get_or_404(name)
return render_template('article.html', article=article, ampPage=True, debug_mode=DEBUG)
If You’re Using Frozen-Flask
Typically, you would just need to create a route for the AMP version of your page. However, if you’re using Frozen-Flask for your static setup, you need to register the generator for it to create the AMP version of the page. If you haven’t registered a generator before, it’s relatively straightforward.
@freezer.register_generator
def amp_URLs():
for article in flatpages:
yield 'amp_article', { 'name': article.meta.get('pageID'), 'category': article.meta.get('category')}
What this code does is loops through all the articles in the flatpages list and generate a link pointing to the amp page. It pulls the two variables from the metadata of the article object. Make sure you point the yield statement to the correct method you use for your route.
Setup the Layout File
My layout file I use as a template to set up the overall structure of my site. To get my layout page ready for AMP, there are three different things that I need to change. The logic for everything on this page is driven by the ampPage variable.
Adding AMP to the HTML tag
Part of the AMP specification includes adding the word amp to the HTML tag. This can easily be accomplished by adding an if statement that adds AMP if the page being called is an AMP page. The code looks like this:
<html lang="en-US"{%- if ampPage -%} amp{%- endif -%}>
This one’s pretty straightforward as it’s a simple logic statement that checks the ampPage variable we passed through using the route.
Removing Custom Javascript Files
To remove custom Javascript files, I use the same concept as before, but this time to only include the Javascript files if the page being called is not an AMP page.
{% if not ampPage %}
<script src="{{ url_for('static', filename='js/custom.js', _external=True) }}"></script>
{% endif %}
If you’re not familiar with url_for
, it’s a Flask concept used for generating a file point to a specific target. In this instance, it’s pointing to the /static/js/custom.js file of my site. This code limits my custom Javascript file only to be included when the page being called is a regular article page.
Adding AMP Analytics
The final thing I need to include is a way to keep analytics when my AMP pages are visited. AMP analytics are driven by JSON, so you just need to add the following code:
{% if ampPage %}
<amp-analytics type="googleanalytics">
<script type="application/json">
{
"vars": {
"Account”:”ANALYTICS_ACCOUNT_ID”
},
"triggers": {
"trackPageView":{
"on":"visible",
"request":"pageview"
}
}
}
</script>
</amp-analytics>
{% endif %}
This code sets the account I’m using, what triggers the JSON data to be sent, and which events to monitor. In this case, I’m tracking page views which are triggered by a user viewing the page.
Setup the Article File
Finally, we need to set up the article template that will show your AMP page when someone visits an AMP version of your page. This page will include the AMP boilerplate, add the CSS for styling the article page, set the links for the canonical and AMP versions of the page, and change the image used for the article.
All of my code is contained in content blocks set in the layout template. I have content blocks for nearly every section of the page, which allows me to override or add to what is already part of the content. I tried following a basic inheritance strategy to make it easy to add new generic or custom pages.
Inside the Head Content Block
This section is used for the boilerplate, CSS, and link code. It uses an if
statement to check if the page being requested is the AMP version. For the AMP version, it overwrites the head content block in the layout template. This allows the AMP version to replace the code that loads the CSS files for my main page.
{%- block head -%}
{% if ampPage is True %}
[AMP_BOILER_PLATE]
<style amp-custom>
[CUSTOM_AMP_STYLE]
</style>
{% else %}
<link rel="amphtml" href="{{ url_for('amp_article', name=article.pageID, category=article.category, _external=True) }}">
{{ super() }}
{%- endif -%}
<link rel="canonical" href="{{ url_for('article', name=article.pageID, category=article.category, _external=True) }}">
{%- endblock head -%}
As a quick note, replace [AMP_BOILER_PLATE] with the correct AMP boilerplate code, and replace [CUSTOM_AMP_STYLE] with your own CSS styles.
You’ll notice that the else
statement calls the super block, otherwise known as the parent. This ensures that the main page includes the CSS files. The other line in the else
statement will create a link pointing to the amphtml
version of the page.
I also included the canonical link outside the if
statement so that it will run for either version of the page. It’s a best practice to put a canonical link on the main version of a page. Even if you only have one version of a page, it’s best practice to put in the canonical link.
Replacing the Image
To replace the image, I just use a logical if
statement to use the <amp-image>
version of the code. I chose to use the 800-pixel version of the page as a compromise between content quality and file size. Also, if you have webp versions of your image, AMP doesn’t allow webp. You need to use either JPG, GIF, or PNG images.
{%- if ampPage -%}
<amp-image content=“” width=“800” height=“506” layout=“responsive”></amp-image>
{%- else -%}
[NORMAL_PICTURE_CODE]
{%- endif -%}
Testing the AMP Pages
Once your code is finally implemented, you’re not quite done. Any good developer knows that you need to troubleshoot your implementation to make sure that you don’t have any problems with your code. There are two different ways that you can test your AMP pages.
Using the AMP Project Validator
If you don’t want to install anything on your computer - or can’t - you can use the one provided by the AMP project by going to the AMP Project Validator website.
This works reasonably well, but you’ll have to run your site then copy-paste your code into the validator. It doesn’t take much time, but it can be a bit tedious. I prefer to use more automated solutions to make development easier. Luckily, the AMP Project created a Chrome Extension that can do just that.
Using Chrome with the AMP Project Validator Extension
If you aren’t using Chrome, I would recommend downloading it even if it’s just for testing. Using Chrome with the AMP validator will allow you to view your AMP page and run validation checks on it. This makes it much easier to validate your AMP code to make sure everything is working smoothly without having to copy-paste.
The Full Code
Now that we’ve gone through why you need AMP and the code that I use for my implementation, I’ll include the full code you need. Remember that I’m using python on the Flask framework. I also use Flask-FlatPages and Frozen-Flask to generate my static site. If you’re using a different framework, you’ll be able to use these same concepts, you’ll just need to update the code for your environment.
The Application File
Place the following code in your app.py file.
Freezer Register
@freezer.register_generator
def amp_URLs():
for article in flatpages:
yield 'amp_article', { 'name': article.meta.get('pageID'), 'category': article.meta.get('category')}
AMP Page Route
@app.route('/<category>/<name>/amp.html')
def amp_article(name, category):
article = flatpages.get_or_404(name)
return render_template('article.html', article=article, ampPage=True, debug_mode=DEBUG)
The Layout Template
This code will go in your template file.
<html lang="en-US"{%- if ampPage -%} amp{%- endif -%}>
Place logic to block your Javascript files.
{% if not ampPage %}
<script src="{{ url_for('static', filename='js/custom.js', _external=True) }}"></script>
{% endif %}
Load your AMP analytics.
{% if ampPage %}
<amp-analytics type="googleanalytics">
<script type="application/json">
{
"vars": {
"Account”:”ANALYTICS_ACCOUNT_ID”
},
"triggers": {
"trackPageView":{
"on":"visible",
"request":"pageview"
}
}
}
</script>
</amp-analytics>
{% endif %}
The Article Template
This code is what goes in your article template file.
Replace the Head Block
{%- block head -%}
{% if ampPage == True %}
<link rel="canonical" href="{{ url_for('article', name=article.pageID, category=article.category, _external=True) }}">
<script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
<script async custom-element="amp-sidebar" src="https://cdn.ampproject.org/v0/amp-sidebar-0.1.js"></script>.
<script async src="https://cdn.ampproject.org/v0.js"></script>
<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>
<style amp-custom>
</style>
{% else %}
<link rel="amphtml" href="{{ url_for('amp_article', name=article.pageID, category=article.category, _external=True) }}">
{{ super() }}
{% endif -%}
<link rel="canonical" href="{{ url_for('article', name=article.pageID, category=article.category, _external=True) }}">
{%- endblock head -%}
Change Your Image Tag
{%- if ampPage -%}
<span class="markdown-image image">
<amp-img content="{{ url_for('static', filename='img/%s/%s/800.jpg' % (article.category, article.pageID), _external=True) }}" src="{{ url_for('static', filename='img/%s/%s/800.jpg' % (article.category, article.pageID), _external=True) }}" width="800" height="506" layout="responsive"></amp-img>
</span>
{%- else %}
[NORMAL_PICTURE_CODE]
{%- endif -%}
Final Thoughts
AMP pages are something that you can quickly and easily implement on your static site. AMP pages offer a superior experience for mobile users. The number of mobile users is growing, and embracing technologies such as AMP will help you create a better user experience for a large number of users.
If you have any questions about AMP, python, Flask or any of the Flask libraries that I use, please let me know. I’m trying to create content that is helpful, and any feedback I get from you is invaluable. Thank you!