Allows running custom JavaScript to render UI.

Required Script
<script async custom-element="amp-script" src=""></script>


The amp-script component allows you run custom JavaScript to render UI elements, such as a React component.

A simple example

An amp-script element can load JavaScript in two ways: - Remotely, from a URL to a JavaScript file. - Locally, from a script[type=text/plain][target=amp-script] element on the page.

Load JavaScript from a remote URL

Use the src attribute to load remote JavaScript.

<amp-script layout="container" src="">
  <button>Hello amp-script!</button>

If src points to a cross-origin URL, then a "script hash" must also be added to the document head.

Load JavaScript from a local element

Use the script attribute to reference a local script element by id.

<!-- Using the "script" attribute also requires adding a "script hash" to the document head. -->


<amp-script width=200 height=50 script="hello-world">
  <button>Hello amp-script!</button>

<!-- Also add [target="amp-script"] to the <script> element. -->
<script id="hello-world" type="text/plain" target="amp-script">
  const btn = document.querySelector('button');
  btn.addEventListener('click', () => {
    document.body.textContent = 'Hello World!';

amp-script elements that have a script or cross-origin src attribute require a "script hash". Script hashes are specified in a <meta name="amp-script-src" content="..."> element in the document head.

A console error will be thrown with the expected content value -- you can copy/paste from the error to create the appropriate <meta> tag.

How does it work?

amp-script runs your custom JavaScript in a Web Worker that contains a virtual DOM. When your JavaScript code modifies this virtual DOM, amp-script forwards these changes to the main thread and applies them to the amp-script element subtree.

For example, adding an element to document.body:

// my-script.js
const p = document.createElement('p');
p.textContent = 'I am added to the body!';

Will be reflected on the page as a new child of the amp-script element:

<amp-script src="" width=300 height=100>
  <p>I am added to the body!</p>

Under the hood, amp-script uses @ampproject/worker-dom. For design details, see the "Intent to Implement" issue.

State manipulation

amp-script supports getting and setting amp-state JSON via JavaScript.

This enables advanced interactions between amp-script and other AMP elements on the page via amp-bind bindings. These elements can be inside (descendants) or outside (non-descendants) of the amp-script element.

AMP.setState() requires the amp-bind extension script to be included in the document head.

 * Deep-merges `json` into the current amp-state.
 * @param {!Object} json A JSON object e.g. must not contain circular references.
AMP.setState(json) {}

 * Asynchronously returns amp-state.
 * @param {string=} expr An optional JSON expression string e.g. "".
 * @return {!Promise<!Object>}
AMP.getState(expr) {}
Example with WebSocket and AMP.setState()
<amp-script width=1 height=1 script="webSocketDemo">

  <amp-state> doesn't support WebSocket URLs in its "src" attribute,
  but we can use <amp-script> to work around it. :)
<script type="text/plain" target="amp-script" id="webSocketDemo">
  const socket = new WebSocket('wss://websocket.example');
  socket.onmessage = e => {


Allowed APIs

Currently, most DOM elements and their properties are supported. DOM query APIs like querySelector have partial support. Browser APIs like History are not implemented yet. See the API compatibility table for details.

If there's an API you'd like to see supported, please file an issue and mention @choumx and @kristoferbaxter.

Size of JavaScript code

amp-script has the following restrictions on JavaScript file size:

  • Maximum of 10,000 bytes per amp-script element that uses a local script via script[type=text/plain][target=amp-script].
  • Maximum total of 150,000 bytes for all amp-script elements on the page.

User gestures

amp-script generally requires a user gesture to apply changes triggered by your JavaScript code to the page (we call these "mutations"). This requirement helps avoid poor user experience from unexpected content jumping.

The rules for mutations are as follows:

  1. Mutations are always accepted for five seconds after a user gesture.
  2. The five second interval is extended if the author script performs a fetch() as a result of the user gesture.
  3. Mutations are always accepted for amp-script elements with [layout!="container"] and height < 300px.

Security features

Since custom JS run in amp-script is not subject to normal Content Security Policy, we've included some additional measures that are checked at runtime:

  1. Same-origin src must have Content-Type: application/javascript.
  2. Cross-origin src and script must have matching script hashes in a meta[name=amp-script-src] element in the document head. A console error will be emitted with the expected hash string.

Example of script hashes:

    A meta[name="amp-script-src"] element contains all script hashes for
    <amp-script> elements on the page, delimited by spaces.
    A "src" attribute with a cross-origin URL requires adding a script hash.

    If the hash of remote.js's contents is "fake_hash_of_remote_js",
    we'll add "sha384-fake_hash_of_remote_js" to the <meta> tag above.
  <amp-script src="cross.origin/remote.js" layout=container>

    A "script" attribute also requires adding a script hash.

    If the hash of #myScript's text contents is "fake_hash_of_local_script",
    we'll add "sha384-fake_hash_of_local_script" to the <meta> tag above.
  <amp-script script=myScript layout=container>
  <script type=text/plain target=amp-script id=myScript>
    document.body.textContent += 'Hello world!';

The JavaScript size and script hash requirements can be disabled during development by adding a development attribute to an amp-script element.



For executing remote scripts.

The URL of a JS file that will be executed in the context of this <amp-script>. The URL's protocol must be HTTPS and the HTTP response's Content-Type must be application/javascript.


For executing local scripts.

The id of a script[type=text/plain][target=amp-script] element whose text content contains JS that will be executed in the context of this <amp-script>.

sandbox (optional)

Applies extra restrictions to DOM that may be mutated by this <amp-script>. Similar to the iframe[sandbox] attribute, the value of the attribute can either be empty to apply all restrictions, or space-separated tokens to lift particular restrictions:

  • allow-forms: Allows form elements to be created and modified. AMP requires special handling to prevent unauthorized state changing requests from user input. See amp-form's security considerations for more detail.

max-age (optional)

Requires the script attribute.

The max-age attribute specifies the maximum lifetime in seconds the local script is allowed to be served from the time of signed exchange (SXG) publishing.

The value of max-age should be chosen carefully:

  • A longer max-age increases the potential security impact of a SXG downgrade.

  • A shorter max-age may prevent inclusion in AMP Caches that have a minimum SXG lifetime.

If you don't publish signed exchanges, max-age does nothing.

development (optional, invalid)

A boolean attribute that disables the JS size and security constraints for a more convenient development experience.

This attribute is not allowed by the AMP Validator and should not be used on pages in production.

common attributes

This element includes common attributes extended to AMP components.


There are several types of runtime errors that may be encountered when using amp-script.

"Maximum total script size exceeded (...)"

amp-script limits the size of the JS source that may be used. See Size of JavaScript code above.

"Script hash not found."

Local scripts and cross-origin src require adding a special <meta> tag to be used. See Security features above.

"amp-script... was terminated due to illegal mutation"

To avoid unexpected content jumping, amp-script generally requires user gestures for DOM changes. See User gestures above.

Need more help?

You've read this document a dozen times but it doesn't really cover all of your questions? Maybe other people felt the same: reach out to them on Stack Overflow.

Go to Stack Overflow
Found a bug or missing a feature?

The AMP project strongly encourages your participation and contributions! We hope you'll become an ongoing participant in our open source community but we also welcome one-off contributions for the issues you're particularly passionate about.

Go to GitHub