AMP provides a way to build web pages that render with reliable and fast performance. AMP includes many responsive and interactive components, which can be used to create product category pages, product detail pages, checkout flows, and wishlists in AMP.

What you'll build

In this codelab, you'll build upon an existing product page (from the Advanced Interactivity codelab) and add common features that you expect to see on product pages like, favorite buttons, personalized experiences for users, date selectors, and more.

What you'll learn

What you'll need

Tooling for serving content

We will use Node.js to run a local HTTP server to serve our AMP page. Check the Node.js website for instructions on how to install Node on your operating system.

Our tool of choice to serve content locally will be serve, which is a Node.js-based static content server. To install it, run:

npm install -g serve

Download the code

Download the starter code for the codelab either as a ZIP file:

Download

Or via git:

git clone https://github.com/googlecodelabs/amp-e-commerce.git

Install dependencies

Unzip the archive file (if necessary) and navigate into the directory. Install the dependencies by running npm install.

cd amp-e-commerce
npm install

Run the development server

Start the development server with node.js:

node app.js

Navigate to http://localhost:3000 in your web browser to see the AMP page running!

At this point we have a functioning AMP product page. Let's add a favorite button to let the user select a product and remember their selection upon returning to the a page, similar to a wishlist. We'll use two button styles to indicate selected (filled black heart) vs unselected (grey border heart). Add the following code below the </p> closing <p class="dots"> in static/index.html file:

<input type="submit" class="heart-border" [class]="favorite ? 'heart-fill' : 'heart-border'" value="" aria-label="Favorite Toggle">

Then add the following css into the amp-custom css section:

.heart-loading,
.heart-loading[placeholder] {
 background: url('data:image/svg+xml;utf8,<svg fill="%23cccccc" height="48" viewBox="0 0 24 24" width="48" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3zm-4.4 15.55l-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05z"/></svg>');
}
input[type=submit].heart-border, input[type=submit].heart-fill, input[type=submit].heart-loading {
      width: 48px;
      height: 48px;
      cursor: pointer;
      border: none;
      margin: 4px;
      transition: background 300ms ease-in-out;
}
.heart-fill {
 background: url('data:image/svg+xml;utf8,<svg fill="%23000000" height="48" viewBox="0 0 24 24" width="48" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>');
}
.heart-border {
 background: url('data:image/svg+xml;utf8,<svg fill="%23000000" height="48" viewBox="0 0 24 24" width="48" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3zm-4.4 15.55l-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05z"/></svg>');
}
#favorite-failed-message {
 display: flex;
 justify-content: space-between;
 position: fixed;
 bottom: 0;
 left: 0;
 right: 0;
 margin: 0 auto;
 width: 100%;
 max-width: 450px;
 background: #323232;
 color: white;
 padding: 1rem 1.5rem;
}
#favorite-failed-message div {
 color: #64dd17;
 margin: 0 1rem;
 cursor: pointer;
}

Check your code by running http://localhost:3000/index.html. You should now see a heart-shaped button.

Making the heart interactive

We need to make the heart interactive so that the style changes when it's clicked, and so that it posts an update for each change. To do this, we'll need to wrap the input into an amp-list component and then put all of it inside a form.

Replace the <input> element you just added with the following code:

<amp-state id="favorite" credentials="include" src="/favorite">
</amp-state>
<form method="post" action-xhr="/favorite" target="_top"
on="submit:AMP.setState({favorite:!favorite}),favorite-failed-message.hide; 
submit-error:AMP.setState({favorite:!favorite}),favorite-failed-message.show">
 <amp-list width="56" height="56" credentials="include" items="." single-item src="/favorite">
  <template type="amp-mustache">
   <input type="submit" [class]="favorite ? 'heart-fill' : 'heart-border'" value="" aria-label="Favorite Toggle"> 
  </template>
  <div placeholder>
   <input type="submit" disabled class="heart-loading" value="" aria-label="favorite placeholder">
  </div>
 </amp-list>
</form>
<div id="favorite-failed-message" hidden>Error: Could not favorite.
 <div on="tap:favorite-failed-message.hide" tabindex="0" role="button">CLOSE
 </div>
</div>

As we are adding the amp-list component, we must add the required script for amp-list. Add the following import in the head of the page:

<script async custom-element="amp-list" src="https://cdn.ampproject.org/v0/amp-list-0.1.js"></script>

Now, let's look at the code we added.

We initialize the button state from a JSON endpoint by using amp-state. As we are using cookies to identify the user, we need to add the credentials="include" attribute.

The button is embedded inside an amp-list, which allows us to dynamically render the button based on whether the user already liked something or not. We don't make use of amp-list's template substitution mechanism ("{{..}}"), but rather use amp-bind to bind the button state to the amp-state ("[class]=...").

While the amp-list is loading, we show a placeholder icon by specifying a placeholder attribute in a child element of amp-list.

There are a few things that happen when the user presses the favorite button:

The completed code for this section can be found in GitHub (index-step1.html).

Summary

In this section, we'll explore different ways of building personalized experiences in AMP.

Signed-in users

In this section, we'll use the amp-access component to facilitate sign-in and we'll change the page content based on whether the user has signed in or not. First, let's import amp-access and amp-analytics by adding the following scripts to the head section:

<script async custom-element="amp-access" src="https://cdn.ampproject.org/v0/amp-access-0.1.js"></script>
<script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>

For amp-access, we start by specifying the endpoints that you want to use for authorization, pingback and login/logout. You can also include a fallback response in case authorization fails. Add the code below in the page's head section:

<script id="amp-access" type="application/json">
 {
 "authorization":
"/authorization?rid=READER_ID&url=CANONICAL_URL&ref=DOCUMENT_REFERRER&_=RANDOM",
 "noPingback": "true",
 "login": {
  "sign-in": "/login?rid=READER_ID",
  "sign-out": "/logout?rid=READER_ID"
  },
  "authorizationFallbackResponse": {
   "error": true,
   "loggedIn": false
  }
 }
</script>

Now we can start marking up the page so that certain content is only visible to logged-in users.

To do this, we'll use the amp-access attribute, which controls the visibility of each component. Add the following code just inside the <div id="container">:

<section amp-access="loggedIn">
 <template amp-access-template type="amp-mustache">
   <p> Hello {{name}}! Check out these deals we found for you! </p>
   <div class="deals-container">
    {{#deals}}
     <a href="{{link}}">
      <amp-img src="{{img}}" width=100 height=100></amp-img>
      <p class="deal-name mdl-color-text--black">{{name}}</p>
     </a>
    {{/deals}}
   </div>
 </template>
<a class="mdl-button mdl-js-button mdl-js-ripple-effect" href="/logout">Logout</a>
</section>
<section amp-access="NOT loggedIn" amp-access-hide>
 <span> Log-in or sign up to see deals </span>
 <button class="mdl-button mdl-js-button mdl-js-ripple-effect" on="tap:amp-access.login-sign-in">Login</a>
</section>

Let's look at the code we've added.

The amp-access expression that we defined in the section must evaluate to a boolean. The loggedin value needs to be set by the response to the authorization request.

You can return as much information as you want in the authorization request, such as the user name. In this example, if the user is logged-in, we also return information about deals. We welcome the user by printing their name and then iterate through the deals.

Next, we have a section that only displays if the user is not logged in. This section prompts the user to log in. By using the on attribute with the action tap:amp-access.login-sign-in we open the log-in dialog when the element is clicked. Recall that we defined the endpoint for this in the amp-access configuration. When you try out the login functionality, you can try with different email accounts; we configured test1@example.com to receive different deals. Logout then login with this different account.

To improve the style of the page, add the following css for the deals section:

.deals-container {
 display: flex
}
.deal-name {
 text-align: center;
 font-family: "Roboto","Helvetica","Arial",sans-serif;
}
a[href] {
 text-decoration: none;
}

Try out your code. If you're stuck, check out the completed code for this section on GitHub (index-step2-a.html).

Change content based on user's location

Next, let's improve our sample to provide catered content based on the user's location.

You can use amp-list to render geo-specific data, such as the user's location. Add the following code to the bottom of the page:

<amp-list src="/location-specific-results.json" width="auto" height="32" single-item items=".">
 <template type="amp-mustache">
  <p class="location"> You are in {{location}} </p>
 </template>
</amp-list>

Add the following css to the custom style section:

.location {
 padding: 16px 0 0 16px;
}

In the amp-list server endpoint, you can determine the location based on the client IP address, then modify the JSON response based on the location. Then , you can use the amp-mustache template to dynamically render the location based on that response.

As we are running this sample in localhost, we are using google.com as ip, so you should always see Mountain View as a location result.

Now, reload the page and scroll to the bottom to see that your location displays:

The completed code for this section can be found in GitHub (index-step2-b.html).

Vary content for selected country

You can also update the page content based on user input.

In the following code, we update the availability of next-day shipping based on the selected country. Add the following code after the <div class='price'>...</div>:

<amp-list src="/countries.json" width="auto" height="40" layout="fixed-height">
 <template type="amp-mustache">
  <label for="country">Choose country for shipping availability:</label>
  <select name="country" id="country" on="change:AMP.setState({shippingSrc: '/shipping?location=' + event.value})">
   {{#countries}}
    <option value="{{name}}"> {{name}} </option>
   {{/countries}}
  </select>
 </template>
</amp-list>
<amp-list src="/shipping" [src]="shippingSrc" single-item items="." height="25" layout="fixed-height">
 <template type="amp-mustache">
  <p> Shipping next day {{next-day-availability}}  </p>
 </template>
</amp-list>

First, we use an amp-list to populate the countries in the selector, and when a country is selected, we update the value of shippingSrc to be an endpoint specific to the country chosen.

The next amp-list uses shippingSrc to populate the value of the "shipping next day" section. If the customer changes the country again, the value automatically updates.

The completed code for this section can be found on GitHub (index-step2-c.html).

Personalize content for the specific user

In this section we'll talk about personalizing a page, such a home page, based on a user's past behavior. Given that we are here implementing a product page, we won't implement this feature but this feature could be helpful in case you are thinking about implementing your home page in AMP.

Let's say that the user has searched for some products in the past and then returns to the home page, you could display a list of those products to help them pick up where they left off. To achieve this, you can:

Summary

In this section, we'll add a banner to the top of the header, which disappears when the user scrolls. First, let's replace the header with the following code:

<header id="header" >
 <div class="header-advertisement header-menu">
  <a href = ".">This is an advertisement</a>
 </div>
 <div id="header-title" class="mdl-color--black mdl-color-text--white header-menu">
  <amp-img class="menu" src="./img/ic_menu_white_24dp_1x.png" width=24 height=24></amp-img>
  <span class="mdl-color-text--blue">AMP</span>PAREL
  <amp-img class="search" src="./img/ic_search_white_24dp_1x.png" width=24 height=24></amp-img>
 </div>
</header>
<div class="header-menu"></div>

We also need to update the CSS:

#header  {
 position: fixed;
 top: 0;
 width: 100%;
 z-index: 1;
}
.header-menu {
 height: 24px;
 padding: 16px;
 margin: 0;
 text-align: center;
 text-transform: uppercase;
 letter-spacing: 2px;
}
#header-title {
 position: absolute;
 top: 56px;
 left: 0;
 width: 100%;
 z-index: 1;
}
.header-advertisement {
 background: white;
}
#container {
 max-width: 500px;
 padding: 32px 16px 64px 16px;
 margin: auto;
 margin-top: 56px;
}
.search {
 float: right;
 margin-right: 28px;
}

Note that #header, .search and #container were already defined. Please delete the old definitions and use the new definitions above.

Now if you run the app, you should see an advertisement fixed to the top of the page:

By using amp-animation and amp-position-observer, we'll make the advertisement disappear on scrolling.

As we're using the amp-animation and amp-position-observer extensions, add the required scripts to the head section:

<script async custom-element="amp-animation" src="https://cdn.ampproject.org/v0/amp-animation-0.1.js"></script>
<script async custom-element="amp-position-observer" src="https://cdn.ampproject.org/v0/amp-position-observer-0.1.js"></script>

Let's define the animation to shrink and expand the ad. Add this code below <amp-state id="selected">...</amp-state>:

<amp-animation id="shrinkAnim" layout="nodisplay">
 <script type="application/json">
  {
   "duration": "200ms",
   "fill": "both",
   "iterations": "1",
   "direction": "alternate",
   "animations": [
    {
     "selector": "#header",
     "keyframes": [{ "transform": "translateY(-112px)" }]
    },
    {
     "selector": ".header-menu",
     "keyframes": [{ "transform": "translateY(56px)" }]
    }
   ]
  }
 </script>
</amp-animation>
<amp-animation id="expandAnim" layout="nodisplay">
 <script type="application/json">
  {
   "duration": "50ms",
   "fill": "both",
   "iterations": "1",
   "direction": "alternate",
   "animations": [
    {
     "selector": "#header",
     "keyframes": [{ "transform": "translateY(0)"}]
    },
    {
     "selector": ".header-menu",
     "keyframes": [{ "transform": "translateY(0)" }]
    }
   ]
  }
 </script>
</amp-animation>

Let's activate these animations by adding an amp-position-observer. Add the following code below the form tag for the favorite section:

<div id="menu-marker">
 <amp-position-observer on="enter:shrinkAnim.start; exit:expandAnim.start;" layout="nodisplay">
 </amp-position-observer>
</div>

If you reload the page, you should see the ad expand and shrink when you scroll by the <div id="menu-marker">.

The completed code for this section can be found in GitHub (index-step3.html).

Summary

In this section, we'll implement a scroll to top button by combining amp-position-observer and amp-animation.

We use two amp-animation elements to trigger the visibility of the button. The first one makes the button visible and the second one adds the button. Add the following code just before the <header>:

<amp-animation id="showAnim" layout="nodisplay">
 <script type="application/json">
  {
   "duration": "200ms",
   "fill": "both",
   "iterations": "1",
   "direction": "alternate",
   "animations": [
    {
     "selector": "#scrollToTopButton",
     "keyframes": [{ "opacity": "1", "visibility": "visible" }]
    }
   ]
  }
 </script>
</amp-animation>
<amp-animation id="hideAnim" layout="nodisplay">
 <script type="application/json">
  {
   "duration": "200ms",
   "fill": "both",
   "iterations": "1",
   "direction": "alternate",
   "animations": [
    {
     "selector": "#scrollToTopButton",
     "keyframes": [{ "opacity": "0", "visibility": "hidden" }]
    }
   ]
  }
 </script>
</amp-animation>

We'll use amp-position-observer to monitor the user's position in the viewport, and start the animation as soon as the user scrolls. Add the following code just below the animation:

<div id="marker">
 <amp-position-observer on="enter:hideAnim.start; exit:showAnim.start" layout="nodisplay">
 </amp-position-observer>
</div>

We'll need to reference an ID in the header-menu for scrolling, so let's add id="top-page" to <div class="header-menu"></div>:

<div id="top-page" class="header-menu"></div>

Then, let's add a button to the bottom of the page:

<button id="scrollToTopButton" on="tap:top-page.scrollTo(duration=200)" class="scrollToTop mdl-color--black">⌃</button>

In the button, we use the scrollTo action to scroll the page when the button is tapped. Learn more about actions here.

Let's style the button with the following CSS:

.scrollToTop {
 color: #fafafa;
 font-size: 1.4em;
 box-shadow: 0 1px 1.5px 0 rgba(0,0,0,.12), 0 1px 1px 0 rgba(0,0,0,.24);
 width: 50px;
 height: 50px;
 border-radius: 100px;
 border: none;
 outline: none;
 z-index: 9999;
 bottom: 10px;
 right: 10px;
 position: fixed;
 opacity: 0;
 visibility: hidden;
}

Reload the page and scroll to the bottom to see the button appearing. Click the button to see how the page scrolls back to the top.

The completed code for this section can be found in GitHub (index-step4.html).

Summary

In this section, we'll provide a date picker so the user can choose a delivery date.

We'll use the amp-date-picker component, which is experimental, so you need to add a meta tag to the <head> of the document. Once the component is launched, the meta tag will not be needed any more.

<meta name="amp-experiments-opt-in" content="amp-date-picker">

Start by importing the amp-date-picker extension in the head of the page:

<script async custom-element="amp-date-picker" src="https://cdn.ampproject.org/v0/amp-date-picker-0.1.js"></script>

Then, copy the following code below the SIZE section <div class="options sizes"> ... </div>:

<div>
 <h6>DELIVERY DATE:</h6>
 <amp-date-picker type="single" mode="overlay" src="delivery.json" 
on="select:AMP.setState({tooltip: event.id == 'deal'})"
locale="en" format="YYYY-MM-DD" min="2017-10-26" month-format="MMM" input-selector="#delivery">
  <input placeholder="delivery" id="delivery"/>
  <template date-template id="deal" type="amp-mustache">
   {{D}}
   <span class="highlight-square"></span>
  </template>
 </amp-date-picker>
</div>
<div class="tooltip" [class]="'tooltip' + (tooltip ? ' tooltip-show' : '')">Great price </div>

And add the following CSS to the amp-custom style section:

.tooltip {
 visibility: hidden;
 transform: translateY(0);
 position: fixed;
 z-index: 2;
 width: 100%;
 top: 100%;
 height: 50px;
 padding-bottom: 40px;
 color: #fff;
 background-color: #20bf7c;
 text-align: center;
 line-height: 50px;
 font-size: 1.25em;
 font-family: sans-serif;
 left: 0;
}
.tooltip-show {
 visibility: visible;
 animation: slide-in-out 4s linear;
}
.CalendarDay__highlighted_calendar .CalendarDay_button:before, .highlight-square {
 content: '';
 background-color: #20bf7c;
 width: 5px;
 height: 5px;
 position: absolute;
 bottom: 5px;
 margin: auto;
 left: 0;
 right: 0;
}
@keyframes slide-in-out {
 0% { transform: translateY(0) }
 10% { transform: translateY(-100%) }
 90% { transform: translateY(-100%) }
 100% { transform: translateY(0) }
}

We are using an external file called delivery.json, to configure some dates in the date picker as deal dates (we mark them with a green dot). In the template inside the amp-date-picker we style those specific dates with a dot. We use amp-bind to set the variable tooltip with a boolean in case the selected date is one of the deals; we later use the variable tooltip to style a banner which appears from the bottom when the date selected is one of the deals.

Run the code now and try to select dates with and without the green dot to observe the banner appearing.

The completed code for this section can be found in GitHub (index-step5.html).

Summary

In this section, we'll help users find products by implementing a search with autosuggest where the user can type a product name such as "hat", "hoody" or "jeans". To achieve this, we'll use amp-list, amp-form, amp-selector and amp-bind. The amp-form component handles submitting the form and renders the success and error states. In this codelab, we won't implement a navigation to a new page. The amp-list component displays the autosuggest results in an amp-selector for accessibility. Finally, amp-bind glues all the components together to enable interactivity.

First of all, we need some additional CSS to implement the autosuggest overlay and to style the input. Copy the following css into the amp-custom css section:

input[type="submit"].search {
 background-color: #2196f3;
 background-image:
 -webkit-image-set(url(./img/ic_search_white_24dp_1x.png) 1x,
 url(./img/ic_search_white_24dp_2x.png) 2x);
 border: 0 none;
 -webkit-appearance: none;
 border-radius: 0;
 -moz-border-top-right-radius: 4px;
 -moz-border-bottom-right-radius: 4px;
 -webkit-border-top-right-radius: 4px;
 -webkit-border-bottom-right-radius: 4px;
 border-top-right-radius: 4px;
 border-bottom-right-radius: 4px;
 padding: 4px;
 float: right;
 height: 32px;
 background-position: center;
 background-repeat: no-repeat;
 width: 36px;
 position: absolute;
 top: 10px;
 right: 37px;
}
input[type="text"].autosuggest-input {
 padding: 0 8px;
 width: 20vw;
 height: 32px;
 font-size: 100%;
 -moz-box-sizing: border-box;
 -webkit-box-sizing: border-box;
 box-sizing: border-box;
 -webkit-appearance: none;
 border: solid 0px #ccc;
 border-radius: 0;
 -moz-border-top-left-radius: 4px;
 -moz-border-bottom-left-radius: 4px;
 -webkit-border-top-left-radius: 4px;
 -webkit-border-bottom-left-radius: 4px;
 border-top-left-radius: 4px;
 border-bottom-left-radius: 4px;
 position: absolute;
 top: 10px;
 right: 73px;
}
.autosuggest-box {
 position: absolute;
 width: 100%;
 background-color: #fafafa;
 box-shadow: 0px 2px 6px rgba(0,0,0,.3);
 color:black;
}
.autosuggest-container {
 position: relative;
}
.hidden {
 display: none;
}
.suggest {
 width: 80vw;
 max-width: 700px;
 -webkit-box-shadow: 0 0 5px rgba(109,207,246,.5);
 -moz-box-shadow: 0 0 5px rgba(109,207,246,.5);
 box-shadow: 0 0 5px rgba(109,207,246,.5);
 position: absolute;
 position: absolute;
 right: 73px;
 z-index: 1;
 top: 42px;
 text-align: left;
 text-transform: none;
}
.select-option {
 padding: 4px;
 outline: none;
}
#search-form {
 float: right;
}
.site-name {
 position: absolute;
 left: 36%;
}

Let's now add the code. We want to show the search bar in the header next to the search icon.

Copy the following code above the tag <amp-img class="search" src="./img/ic_search_white_24dp_1x.png" width=24 height=24></amp-img>. Notice we are overwriting the tag <span class="mdl-color-text--blue">AMP</span>PAREL.

<div class="site-name">
 <span class="mdl-color-text--blue">AMP</span>PAREL</div>
 <form method="post" action-xhr="" target="_blank" id="search-form"
on="submit: autosuggest-list.hide; submit-success:autosuggest-list.hide, 
AMP.setState({
 products: {
  searchProduct: event.response.name
  }
});
submit-error: autosuggest-list.hide">
  <input name="product" type="text" class="autosuggest-input" on="input-debounced: AMP.setState({
 query: event.value, showDropdown: event.value,
}), autosuggest-list.show;
Tap: AMP.setState({
 query: query == null ? '' : query, showDropdown: 'true',
}), autosuggest-list.show"
[value]="query || ''"value="" required autocomplete="off"/>
   <div class="suggest">
    <div class="autosuggest-container hidden"
[class]="(showDropdown && query) ?
'autosuggest-container' :
'autosuggest-container hidden'">
     <amp-list class="autosuggest-box" layout="fixed-height" height="120" items="." src="/autosuggest/search_product" [src]="query ? autosuggest.endpoint + query : autosuggest.emptyAndInitialTemplateJson" id="autosuggest-list">
      <template type="amp-mustache">
       <amp-selector id="autosuggest-selector" keyboard-select-mode="focus" layout="container"
on="select: AMP.setState({query: event.targetOption,
 showDropdown: false,searchProduct: event.targetOption}),autosuggest-list.hide" >
   
     <div class="select-option no-outline" role="option" tabindex="0" on="tap:autosuggest-list.hide" option="{{name}}">{{name}}
        </div>
     </amp-selector>
    </template>
   </amp-list>
  </div>
 </div>
</form>
<input class="search" type="submit" value="">

To implement the search icon, remove the following line:

<amp-img class="search" src="./img/ic_search_white_24dp_1x.png" width=24 height=24></amp-img> as we are using <input class="search" type="submit" value=""> 

The initial values used by amp-bind and helper variables are configured into an amp-state component. Copy the following code below the header section:

<amp-state id="autosuggest">
 <script type="application/json">
  {
   "endpoint": "/autosuggest/search_list?q=",
   "emptyAndInitialTemplateJson": [{
    "query": "",
    "results": []
   }]
   }
 </script>
</amp-state>

When you run the code, you should see a search bar. Try typing the letter ‘h' and you should see suggested search results, like something similar to the image below:

The completed code for this section can be found in GitHub (index-step6.html).

Summary

Congratulation! You've just created a versatile product page in AMP!

We hope this codelab shows you most of the features you may need on a product page.

What's Next?

We know e-commerce pages may require a lot more that what we learned today. You can find more resources on AMPByExample:

And don't forget the AMP documentation website for any doubt on AMP components.

If you have questions, or run into issues, please find us on AMP Slack Channel or create discussion/bug/feature issues on GitHub.