Navigate back to the homepage

Improving User Experience with AMP

Kamlesh Chandnani
March 16th, 2019 · 5 min read

While browsing on mobile have you ever seen the ⚡(lightning bolt) next to a google search result? Hurrah! You’ve discovered AMP!

Google Search Results

Google Search Results

This is another paragraph after the code block.

What is AMP?

AMP stands for Accelerated Mobile Pages, and it was developed to dramatically improve the performance of the mobile web. AMP pages are just web pages that you can link to and are controlled by you.

AMP was designed to give mobile users a much-improved search and browse experience because: content is faster, more engaging, and easier-to-read and they become responsive quickly.

It is further proof of Google’s focus on providing a superior user experience.

Why AMP at Treebo?

AMP pages are not just web pages but they are web pages with very minimal JavaScript(*cough*) and are cached by Google’s AMP cache which is a high performant proxy-based CDN 😲.

Is that the only reason we decided to create AMP pages? Not really, but yes one of the main reasons in addition to the following factors.

Speed is an integral part of designing web pages. It’s always better to optimize your pages for mobile users. Data from ages shows that “about 40% of people will abandon a web page that takes more than 3 seconds to load.”

Pages that are AMP optimized rank better, load faster and they most certainly convert more mobile page views into transacting customers. More details here

With features like JavaScript out-of-the-box caching, better ranking, faster load times we were confident that the overall user experience will be improved and hence we decided to convert our landing pages(a.k.a SEO pages) like Hotel Listing and Hotel Details into AMP pages.


How did we add AMP to our existing stack?

The first thing we did when we were figuring out the implementation of AMP, we headed to the documentation and these were our initial thoughts

  • Did we make the right decision to invest in AMP? 🤔
  • Is it worth the ROI(Return on Investment)? 🤔
  • Will we need a separate team who will own AMP? 🤔
  • How will we maintain it? 🤔
  • Is it going to create friction in how we work and it will impact our deliverables? 😦
  • Will it be a pain doing the same work twice for a normal page as well as AMP page? 🙁

And we had almost given up 😢

But we looked at things closely and decided it was worth it! 🌅

Let me describe how we came up with the decision!

We were using React with SSR (Server Side Rendering) + Rehydration at CSR (Client Side Rendering). So looking at AMP we realized that we need the following things:

  1. The document HTML as per AMP specs.
  2. All the components that we were using should import the relevant amp script tag like amp-img, amp-carousel etc.

Since AMP pages are web pages without any custom JavaScript, which means we just wanted SSR without rehydration, which means we can generate our AMP compliant HTML document with the specific head tags and amp-boilerplate style tags on the server itself.

Now, this gave us productivity boost out of the box i.e. we can use all the existing react components except that I can’t use their client-side lifecycle methods, which isn’t even required because we already do the API calls on the server for SEO 😍.

Wait, there was one more benefit! At Treebo we created our own component library leaf-ui which uses styled components under the hood and styled components also support SSR so we were able to re-use our existing design system as well 🚀.

There’ more to it, Hold on! 😆

Since amp has some specific components for Image (amp-img), Carousel (amp-carousel) and many more they also require certain runtime scripts as well which are provided by AMP itself. So We took this opportunity to enhance our design system to support AMP by exposing amp components with the same APIs as existing components 😍.

Check out our implementation for amp-carousel 👇

Finally, since we were using AMP for our SEO pages and PWA for the rest of the flow we didn’t want to impact our User Experience because of the transition from AMP to PWA, hence we decided to preload our PWA using AMP’s service worker.

Clicking on the “Filters redirects to PWA but since it’s already pre-cached via service worker the redirect feels smooth.

AMP-PWA Transition

AMP-PWA Transition

Now it was the time to address the questions we asked ourselves earlier:

  • Did we make the right decision to invest in AMP? Yes 💯
  • Is it worth the ROI(Return on Investment)? Obviously 💯
  • Will we need a separate team who will own AMP? Nope, people are still writing React Components.
  • How we’ll maintain it? Since everything is plain old React way there was no overhead here 😍

Here are some additional gains we got for free:

  • We didn’t create a new project everything was accommodated in our existing directory structure. To differentiate an AMP page from its original page all our AMP pages ends with*.amp.js.

Directory Structure

  • We didn’t need a separate build system just for AMP, because all our amp pages fell under /amp/ path so we can identify on the server what version to render (AMP/normal).
  • We were able to use components from our design system the same way we were using it for normal pages.
  • Developer Experience is amazing as we don’t have to educate others in the team that AMP is what they have to learn and write because they are still writing React Components. Hence, zero learning curve.

So in a nutshell, our amp pages are nothing but the server-rendered version of our web app following the amp specs 😎.

Outcomes 🚀

The outcomes were worth noticing

  1. All the traffic for our SEO pages is now on AMP.
  2. Load times and time to interactivity are just fantastic.

Lighthouse Score

Lighthouse scores on Fast-3G

Some Hiccups with AMP

There were some hiccups that we came across

1. Making it discoverable

This was really a tough one. We were all set and made our first release and then waited for 3 long days for our amp pages to be cached by Google. But it wasn’t being cached. Now we didn’t know what went wrong.

Digging deep we found out that since Google has not yet started indexing mobile pages and all our amp canonical links

1<link href=”https://www.treebo.com/hotels-in-mumbai/" rel=”canonical”>

were part of our mobile site so that’s the reason they were not getting crawled! *Boom*💥

Learnings: Always add amp canonical links to both your desktop as well as mobile websites.

2. AMP validation errors for non-standard HTML attributes

We had a <Link/> component which was a styled component for <a/> tag

styled-link passing all props to HTML element

This gets rendered as 👇

1<a class=”Link-sc-1kjcqm-0 gfAZrY” href=”/images/perfect-stay/" color=”blue”>Know more</a>

And this fails the amp validation since it’s not a standard HTML attribute.

AMP Validation

But, why was this prop passed as an attribute to the HTML tag? That is because styled-components passes all the props to the HTML tag as attributes. How to fix this? 👇

styled-link with spreading out props

3. All custom styles should be enclosed within <style amp-custom>

If you’re using styled-components or any library that extracts out styles during SSR and injects them in <style></style> tags, you need to be careful since you’ll again encounter AMP validation errors since you should add all your styles under <style amp-custom></style> tag.

1<style amp-custom>
2 .perfect-stay {
3 border-left: 1px solid #e0e0e0;
4 border-right: 1px solid #e0e0e0;
5 border-bottom: 1px solid #e0e0e0;
6 border-radius: 0 0 2px 2px;
7 }
8 .banner {
9 border: 1px solid #fff;
10 }


Since AMP pages are served from Google’s CDN you need to enable CORS by adding AMP specific headers. It’s necessary because when AMP pages try to get resources from your domain like images, API calls will be cross-origin i.e. from AMP CDN domain 👇


to your actual domain 👇


so you need to enable CORS on your server.

You can read more about it here.

Here’s how we implemented it 👇

1import logger from 'utils/logger';
3const allowedOrigins = [
4 __CONFIG__.hostUrl,
5 `${__CONFIG__.hostUrl.replace('-', '--').replace('.', '-')}.cdn.ampproject.org`,
6 `${__CONFIG__.hostUrl}.amp.cloudflare.com`,
7 'https://cdn.ampproject.org',
10const corsMiddlewareAmp = (req, res, next) => {
11 if (__STAGE__ !== 'staging' && __STAGE__ !== 'production') {
12 logger.info('[corsMiddlewareAmp] bypassing since __STAGE__:', __STAGE__);
13 return next();
14 }
16 let origin = '';
17 const ampSourceOrigin = req.query.__amp_source_origin;
18 if (!ampSourceOrigin) {
19 logger.info('[corsMiddlewareAmp] no ampSourceOrigin', ampSourceOrigin);
20 return next();
21 }
23 if (req.header('Amp-Same-Origin') === 'true') {
24 origin = ampSourceOrigin;
25 logger.info('[corsMiddlewareAmp] Amp-Same-Origin is true');
26 } else if (
27 allowedOrigins.includes(req.header('Origin')) &&
28 ampSourceOrigin === __CONFIG__.hostUrl
29 ) {
30 origin = req.header('Origin');
31 logger.info('[corsMiddlewareAmp] Orign is not Host', origin, __CONFIG__.hostUrl);
32 } else {
33 logger.info('[corsMiddlewareAmp] cors validation failure');
34 return res.sendStatus(401);
35 }
37 logger.info('[corsMiddlewareAmp] cors validation success');
39 res.header('Access-Control-Allow-Credentials', 'true');
40 res.header('Access-Control-Allow-Origin', origin);
41 res.header('AMP-Access-Control-Allow-Source-Origin', ampSourceOrigin);
42 res.header('Access-Control-Expose-Headers', 'AMP-Access-Control-Allow-Source-Origin');
44 return next();
47export default corsMiddlewareAmp;


Developer and Debugging Tools

The tooling experience is not that good when you’re working with AMP but it’s manageable with the following tools:

  1. AMP validator chrome extension.
  2. AMP validator to validate your production pages.
  3. You can also append #development=1 to check the errors in the browser console.


Wrap Up! 🗞

This was a long post! Hope you enjoyed reading it 😃. If you or your company have been working with AMP do share with us your experiences and how you’re making an impact! 🚀

Join the Newsletter

Subscribe to get the latest write-ups about my learnings from JavaScript, React, Design Systems and Life

No spam, pinky promise!

More articles from Kamlesh Chandnani

Travelling to Schengen Countries from India?

A complete guide if you're travelling to Schengen countries from India

September 17th, 2018 · 3 min read

Learn the basics of the JavaScript module system and build your own library

Lately we all have been hearing a lot about “JavaScript Modules”

December 8th, 2017 · 8 min read
Link to https://github.com/kamleshchandnaniLink to https://twitter.com/@_kamlesh_Link to https://www.youtube.com/playlist?list=PLpATFO7gaFGgwZRziAoScNoAUyyR_irFMLink to https://linkedin.com/in/kamleshchandnani