Accelerated Mobile Pages (AMP)

Google and Twitter collaborated to develop AMP, an open source framework for creating faster experiences on the mobile web. In a nutshell, it is a simplified set of html and css for mobile.

For this codeshare website I wanted to enable AMP. The first version of this site was built in Umbraco v7, so here is how I did it.

Premise

The basic premise is to have a new template called Amp.cshtml, have a true/false property on your page to enable AMP or not and if it is true provide the link in the head for the AMP version of the page.

Template

Here is the code for the template, it's a bit rough and naive but it might help someone:

@inherits Umbraco.Web.Mvc.UmbracoViewPage
@{
    Layout = null;

    string pageTitle = Model.GetPropertyValue<string>("pageTitle");

    string imgUrl = "";
    if (Model.HasValue("pageBannerImage"))
    {
        imgUrl = Umbraco.AssignedContentItem.GetPropertyValue<IPublishedContent>("pageBannerImage").Url;
    }
}


<!doctype html>
<html amp>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
    <title>@(Model.HasValue("pageTitle") ? pageTitle : Model.Name)</title>
    <link rel="canonical" href="@Model.Url" />
    <script src="https://cdn.ampproject.org/v0.js" async></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>

    <script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
</head>
<body>
    <amp-analytics type="googleanalytics" id="analytics1">
        <script type="application/json">
            {
               "vars": {
                    "account": "UA-12345678-1"
                },
                "triggers": {
                    "trackPageview": {
                        "on": "visible",
                        "request": "pageview"
                    }
                }
            }
        </script>
    </amp-analytics>
    <nav class="amp-wp-title-bar">
        <div>
            <a href="https://mydomain.com">
                mydomain.com
            </a>
        </div>
    </nav>
    <div class="amp-wp-content">
        <h1 class="amp-wp-title">@(Model.HasValue("pageTitle") ? pageTitle : Model.Name)</h1>
        <ul class="amp-wp-meta">
            <li class="amp-wp-byline">
                <span class="amp-wp-author">Paul Seal</span>
            </li>
            <li class="amp-wp-posted-on">
                <time datetime="@(Model.GetPropertyValue<DateTime>("blogPostDate").ToString("yyyy-MM-ddTHH:mm:sszzz", System.Globalization.CultureInfo.InvariantCulture))">
                    @CodeShare.Library.Utility.Social.General.GetRelativeTime(Model.GetPropertyValue<DateTime>("blogPostDate"))
                </time>
            </li>
        </ul>
        <a href="http://www.facebook.com/sharer/[email protected]" rel="nofollow">
            <amp-img src="https://codeshare.co.uk/media/1127/fbamp-1.png" alt="FB-Share" width="105" height="39" class="amp-wp-enforced-sizes"></amp-img>
        </a>
        <a href="http://twitter.com/intent/[email protected](Model.HasValue("pageTitle") ? pageTitle : Model.Name)&[email protected](Model.Url)&via=prjseal" rel="nofollow">
            <amp-img src="https://codeshare.co.uk/media/1128/twamp-1.png" alt="Tw-Share" width="105" height="39" class="amp-wp-enforced-sizes"></amp-img>
        </a>
        <div class="media-credit-container alignnone"><amp-img class="size-full wp-image-264208 amp-wp-enforced-sizes" src="@imgUrl" alt="@Model.Name" width="1200" srcset="@(imgUrl)?width=1200&mode=crop&anchor=center 1200w, @(imgUrl)?width=622&height=350&mode=crop&anchor=center 622w" sizes="(min-width: 800px) 800px, 100vw" height="675"></amp-img></div>
        @Html.GetGridHtml(Model, "contentGrid", "Amp")
    </div>
</body>
</html>

The Grid Partial

As you can see in my example, I am using the grid instead of just a rich text for my content.

If you are using the grid too, you want to create a partial in the folder Views > Partials > Grid and call it Amp.cshtml

Here is the code for it.

@inherits UmbracoViewPage<dynamic>

@if (Model != null && Model.sections != null)
{
    foreach (var section in Model.sections) {            
        foreach (var row in section.rows) {
            @renderRow(row);
        }
    }
}

@helper renderRow(dynamic row){
    foreach ( var area in row.areas ) {
        foreach (var control in area.controls) {
            if (control !=null && control.editor != null && control.editor.view != null ) {
                <text>@Html.Partial("grid/editors/base", (object)control)</text>
            }
        }
    }
}

Rendering the link in the head

In the head of your master template you want to add this:

@if(Model.Content.HasValue("enableAmp") 
    && (bool)Model.Content.GetPropertyValue<bool>("enableAmp"))
{
    <link rel="amphtml" href="@(Model.Content.Url)amp/" />
}

Then when the main page gets rendered, it has the amphtml link in it.

Enable SearchForTemplate

The last thing left to do is to make sure you have alternate templates enabled. To do this you need to have the SearchForTemplate line set in the 404handlers.config like this:

<?xml version="1.0" encoding="utf-8" ?>
<NotFoundHandlers>
    <notFound assembly="umbraco" type="SearchForAlias" />
    <notFound assembly="umbraco" type="SearchForTemplate"/>
    <notFound assembly="umbraco" type="SearchForProfile"/>
    <notFound assembly="umbraco" type="handle404"/>
</NotFoundHandlers>

That should work for you.

Paul Seal

Umbraco MVP and .NET Web Developer from Derby (UK) who specialises in building Content Management System (CMS) websites using MVC with Umbraco as a framework. Paul is passionate about web development and programming as a whole. Apart from when he's with his wife and son, if he's not writing code, he's thinking about it or listening to a podcast about it.

Proudly sponsored by

Moriyama

  • Moriyama build, support and deploy Umbraco, Azure and ASP.NET websites and applications.
AppVeyor

  • CI/CD service for Windows, Linux and macOS
  • Build, test, deploy your apps faster, on any platform.
stkrs

  • Custom stickers for startups, artists and brands.
  • Bespoke easy-apply, removable, custom brand stickers printed in the UK.
elmah.io

  • elmah.io is the easy error logging and uptime monitoring service for .NET.
  • Take back control of your errors with support for all .NET web and logging frameworks.
uSync Complete

  • uSync.Complete gives you all the uSync packages, allowing you to completely control how your Umbraco settings, content and media is stored, transferred and managed across all your Umbraco Installations.