Engineering

September 3, 2020

May 4, 2020

How to develop & implement AMP forms on AMP pages

Table of contents

As a developer, you probably have better things to do than developing a backend for a simple form, and specially, when we talk about AMP, because something that looks as a simple task, can be tricky and time consuming to make it AMP compliance and ending suffering the typical "it worked on my machine" but, in this case, "it worked on my machine, but not on Google's cache".

Add the amp-form extension

To start using forms in your AMP pages, you have to add first the amp-form extension with the following script:

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

This script must be placed between your <head></head> tags, otherwise it won't pass the AMP document validation.

This component provides polyfills for some missing behaviors in browsers, like allowing you to submit your form without reloading the website nor opening a new tab.

Creating your AMP form HTML

This part is very similar to a normal form but, instead of having an action attribute, we have to use action-xhr for a POST request, this makes the browser to send the data using Fetch API when available, and fallback to XMLHttpRequest API for older browsers.

<form action-xhr="https://www.example.com/endpoint">
  <input type="text" name="name" placeholder="Name" required>
  <input type="email" name="email" placeholder="Email" required>
  <input type="submit" value="Submit">
  <div submit-success>Your form was successfully submitted</div>
  <div submit-error>There was an error sending your form. Please try it again.</div>
</form>

Notice there are two sections with submit-success and submit-error attributes. By default, this sections are hidden, they will be shown based on your form response status code so, if we receive a success status code (2XX), it will show the success section, otherwise it will show the error section.

You can also render success or error messages with values from your form response using extended templates like amp-mustache, for example, if we are receiving a form response with a message attribute either as success or error response:

{
  "message": "Thank you! We have received your form successfully."
}

We can add template sections to our HTML with a {{message}} variable:

<form method="post" action-xhr="https://www.example.com/endpoint">
  <input type="text" name="name" placeholder="Name" required>
  <input type="email" name="email" placeholder="Email" required>
  <input type="submit" value="Submit">
  <div submit-success>
    <template type="amp-mustache">
      {{message}}
    </template>
  </div>
  <div submit-error>
    <template type="amp-mustache">
      {{message}}
    </template>
  </div>
</form>

This will replace those variables with the value in our response.

Developing a form endpoint

Now that we already have our frontend ready, it's time to some backend coding. In this example, we will code a small PHP script that receives a POST request and only validates that name and email properties are required, after this, it returns a JSON response with success or non-success status code based on our validation.

And now comes the tricky part, by default, if your form backend is hosted in the same domain as your form, let's say www.example.com, your form will perfectly work but, when using AMP, your page can be served by different domains like https://<your-domain>.cdn.ampproject.org, https://<your-domain>.amp.cloudflare.com, etc. This means that your server endpoint needs to implement CORS security headers, otherwise, when users try to submit your form in Google's AMP cache, it will dramatically fail and you will suffer a "it worked on my machine".

Now that we know the critical points of developing a form endpoint that works for AMP, let's see some code:

<?php
const SUCCESS_CODE = 200;
const SUCCESS_MESSAGE = 'Thank you! We have received your form successfully.';
const ERROR_CODE = 400;
const ERROR_MESSAGE = 'There are some missing values, please review your form.';

$host = isset($_SERVER['HTTP_X_FORWARDED_HOST'])
  ? $_SERVER['HTTP_X_FORWARDED_HOST']
  : $_SERVER['HTTP_HOST'];
header('Content-Type: application/json');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
header('Access-Control-Expose-Headers: AMP-Access-Control-Allow-Source-Origin');
header('AMP-Access-Control-Allow-Source-Origin: https://' . $host);

if (!isset($_POST['name']) || empty($_POST['name'])) {
  $error = [
    'message' => ERROR_MESSAGE;
  ];

  $json = json_encode($error);
  http_response_code(ERROR_CODE);
  die($json);
}

$success = [
    'message' => SUCCESS_MESSAGE;
];

$json = json_encode($success);
http_response_code(SUCCESS_CODE);
die($json);

This code example authorizes any domain that makes a request to our endpoint but, based on your business needs, you maybe would like to add some security adding authorized domains on your headers.

You can also redirect the user on your form response including AMP-Redirect-To header, if you do, remember to include it on your allowed headers:

<?php
header('Access-Control-Expose-Headers: AMP-Access-Control-Allow-Source-Origin, AMP-Redirect-To');
header('AMP-Redirect-To: https://www.example.com/thank-you');

And that's it, now you can use your form in your AMP pages  🚀

You might like to read

Subscribe to our newsletter

Subscribe to our email newsletter to receive article notifications and regular product updates.