29.6 AMP: Accelerated Mobile Pages Output Format
Right, AMP. Let’s have a talk. AMP, or Accelerated Mobile Pages, is Google’s well-intentioned but often controversial project to make the web faster. The idea is simple: you create a version of your page that follows a very strict, stripped-down set of HTML and CSS rules. In return, Google caches it on their servers and serves it from their own domain (usually google.com/amp/...), which makes it load near-instantly on mobile devices. It’s a classic deal with the devil: you get ludicrous speed and potential SEO benefits (it used to be a requirement for the “Top Stories” carousel), but you sacrifice a lot of control over your own content and design.
Hugo, being the pragmatic tool it is, doesn’t judge. It just gives you a way to generate these pages if you decide the trade-off is worth it. It does this through its output formats system, treating AMP as a parallel version of your regular content.
The Core AMP Output Format Configuration
First, you need to tell Hugo you want AMP versions of your pages. You do this in your config.toml (or .yaml/.json) by defining a new output format. This is non-negotiable; Hugo won’t just guess you want this.
[outputs]
home = ["HTML", "RSS"]
# This says: for regular pages, output both HTML and AMP
page = ["HTML", "AMP"]
This configuration is the engine. It tells Hugo: “For every single page on my site, please generate two versions: the standard index.html in its folder, and an amp.html.” You can verify this by running hugo and looking in your public folder. You’ll see something like public/my-post/index.html and public/my-post/amp/index.html.
The AMP Single Template (single.html)
Now, the fun part: the template. You can’t just use your regular layouts/_default/single.html. AMP is a different language, essentially. You need a dedicated template. Hugo will look for a template specifically for the AMP output format. The convention is to create a template with the base name and the output format as a suffix: single.amp.html.
Create this file at layouts/_default/single.amp.html. This is where you build the cage that your content will live in. The AMP spec is brutally strict, so your template must start with a specific doctype and include the AMP JS library.
<!doctype html>
<html ⚡>
<head>
<meta charset="utf-8">
<title>{{ .Title }}</title>
<link rel="canonical" href="{{ .Permalink }}">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<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>
<script async src="https://cdn.ampproject.org/v0.js"></script>
<!-- Your other AMP-compliant styles go here -->
<style amp-custom>
body {
font-family: sans-serif;
margin: 2em;
}
/* Inline ALL your CSS. Yes, all of it. No external stylesheets allowed. */
</style>
</head>
<body>
<h1>{{ .Title }}</h1>
{{ .Content }}
<!-- And that's about all the excitement you're allowed to have! -->
</body>
</html>
See that ⚡ in the <html> tag? That’s not a typo; it’s the AMP validator’s way of knowing you’re playing by its rules. The amp-boilerplate style block is a hilarious but required bit of CSS sorcery that hides content until the AMP JS is loaded—a necessary evil to prevent unstyled content from flashing. The most important line is <link rel="canonical" href="{{ .Permalink }}"> which points back to your real page, telling Google which version is the master copy.
Handling Images and Other Media
This is where the absurdity often peaks. You can’t use a regular <img> tag. Oh no. You must use the special, AMP-approved component <amp-img>. It’s like <img> but with more attributes and a requirement to explicitly define its width and height. This lets the AMP system calculate the layout before anything loads, which is the secret sauce to its performance, but it’s a pain to manage.
<amp-img src="{{ .Params.hero_image }}"
width="600"
height="400"
layout="responsive"
alt="My amp-image">
</amp-img>
If you forget the width and height, the validator will throw a tantrum. The layout="responsive" attribute is your friend for making it scale nicely. The same goes for video (<amp-video>) and iframes (<amp-iframe>). Everything is a custom element.
The Validation Gauntlet
Before you deploy, you must validate your AMP pages. You can’t just eyeball it. The rules are too numerous and pedantic. The easiest way is to run your site with hugo server, navigate to a page’s /amp/ version (e.g., http://localhost:1313/my-post/amp/), and append #development=1 to the URL. This triggers the AMP validator, which will lovingly dump a list of all your sins directly into the browser console.
The most common pitfalls?
- Invalid CSS: Using a
!importantqualifier or a disallowed selector. Your CSS must be under 75KB and, again, entirely inline. - Non-AMP JS: Any JavaScript you write is forbidden. You can only use the predefined AMP components. This is the entire point, but it’s the biggest wrench in your workflow.
- Missing dimensions on media, as mentioned.
So, should you use it? My honest opinion: the web has largely moved on. Core Web Vitals and improvements in browsers, CDNs, and frameworks have made it possible to get AMP-like speed without AMP’s constraints. The SEO benefit is also not what it once was. But if you’re targeting a specific audience that’s predominantly on slow mobile connections, or you absolutely need that last ounce of perceived performance, Hugo gives you a clean, powerful way to implement it without tearing your entire site apart. Just be ready for the validator to become your new, very nitpicky best friend.