Gathering detailed insights and metrics for @ampproject/toolbox-optimizer
Gathering detailed insights and metrics for @ampproject/toolbox-optimizer
Gathering detailed insights and metrics for @ampproject/toolbox-optimizer
Gathering detailed insights and metrics for @ampproject/toolbox-optimizer
A collection of AMP tools making it easier to publish and host AMP pages.
npm install @ampproject/toolbox-optimizer
Typescript
Module System
Node Version
NPM Version
84.9
Supply Chain
97.4
Quality
83.3
Maintenance
50
Vulnerability
100
License
HTML (78.83%)
JavaScript (15.43%)
TypeScript (3.36%)
CSS (2.36%)
Handlebars (0.01%)
Dockerfile (0.01%)
Total Downloads
77,498,201
Last Day
3,985
Last Week
50,608
Last Month
214,901
Last Year
2,225,482
Apache-2.0 License
453 Stars
1,312 Commits
248 Forks
41 Watchers
61 Branches
111 Contributors
Updated on Jun 30, 2025
Minified
Minified + Gzipped
Latest Version
2.10.1
Package Id
@ampproject/toolbox-optimizer@2.10.1
Unpacked Size
244.92 kB
Size
57.71 kB
File Count
50
NPM Version
10.8.1
Node Version
20.12.2
Published on
Jun 14, 2024
Cumulative downloads
Total Downloads
17
AMP Optimizer is a tool to simplify creating AMP pages and improve AMP rendering performance. AMP Optimizer implements AMP performance best practices and supports AMP server-side-rendering. By default, it will perform the following optimizations:
amp-script
code.The performance optimizations can improve page rendering times by up to 50%. You can read more about the potential performance gains in this blog post. To give it a try, check out the online playground.
Good to know:
Install via:
npm install @ampproject/toolbox-optimizer
Minimal usage:
1const AmpOptimizer = require('@ampproject/toolbox-optimizer'); 2 3const ampOptimizer = AmpOptimizer.create(); 4 5const originalHtml = ` 6<!doctype html> 7<html ⚡> 8 ... 9</html>`; 10 11ampOptimizer.transformHtml(originalHtml).then((optimizedHtml) => { 12 console.log(optimizedHtml); 13});
You can find a sample implementation here. If you're using express to serve your site, you can use the AMP Optimizer Middleware.
AMP Optimizer can be used via the AMP Toolbox CLI:
1npm install @ampproject/toolbox-cli -g 2amp optimize myFile.html
or run without installation via npx
:
1npx @ampproject/toolbox-cli optimize myFile.html
Options are passed when creating a new AMP Optimizer instance:
1const ampOptimizer = AmpOptimizer.create({ 2 verbose: true 3}); 4...
Available options are:
autoAddMandatoryTags
Automatically inject any missing markup required by AMP.
autoAddMandatoryTags
[true|false]
true
autoExtensionImport
Automatically import any missing AMP Extensions (e.g. amp-carousel).
autoExtensionImport
[true|false]
true
esmModulesEnabled
Enables the smaller ESM module version of AMP runtime and components:
<script async nomodule src="https://cdn.ampproject.org/v0.js"></script>
<script async src="https://cdn.ampproject.org/v0.mjs" type="module" crossorigin="anonymous"></script>
Warning: This is incompatible with AMP Packager unless using a prerelease version containing this commit.
esmModulesEnabled
[true|false]
true
extensionVersions
Specify version numbers to use for automatically imported Extensions. If not defined, default to latest.
Example:
1const ampOptimizer = AmpOptimizer.create({ 2 extensionVersions: { 3 'amp-twitter': '0.1', 4 }, 5});
extensionVersions
OBJECT
{}
format
Specifies the AMP format of the input file. Defaults to AMP
.
format
[AMP|AMP4EMAIL|AMP4ADS]
AMP
imageBasePath
Specifies a base path used to resolve an image during build,
this can be a file system path or URL prefix. You can also pass a function
(imgSrc, params) => '../img/' + imgSrc
for dynamically calculating the image path.
imageBasePath
STRING|FUNCTION
imageOptimizer
Enable automated image srcset
generation by providing a function for calculating srcset
URLs for a given image src
. The function should return a URL pointing to a version of the src
image with the given width
. If no image is available, it should return a falsy value.
Example:
1const ampOptimizer = AmpOptimizer.create({ 2 imageOptimizer: (src, width) => `${src}?width=${width}`, 3});
imageOptimizer
FUNCTION
lts
Use long-term stable URLs for downloading the AMP runtime and components.
lts
[true|false]
false
markdown
This transformer adds out-of-the-box markdown support. This allows using AMP Optimizer to convert HTML documents created from Markdown files into valid AMP. A typical conversion flow would be:
README.md => HTML => AMP Optimizer => valid AMP
The only thing this transformer does is converting <img>
tags into
either amp-img
or amp-anim
tags. All other Markdown features are
already supported by AMP. The transformer will try to resolve image
dimensions from the actual files. Images larger than 320px will automatically
get an intrinsic layout. For image detection to work, an optional dependency
probe-image-size
needs to be installed via NPM.
markdown
[true|false]
false
minify
Minifies the generated HTML output and inlined CSS.
minify
[true|false]
true
Warning: this setting is not recommended when running AMP Optimizer in your backend on every request as execution time can increase by up to 7x.
optimizeAmpBind
Enables a considerably faster scanning method in amp-bind
, by injecting a i-amphtml-binding
attribute on all elements with a bound attribute.
optimizeAmpBind
[true|false]
true
optimizeHeroImages
Enables hero image optimization. Hero images will either be auto detected or you can explicitly mark these by adding the data-hero
attribute:
<amp-img data-hero src="foo.jpg" ...>
The maximum number of hero images that can be marked up using data-hero
is 2
. This number can be increased using the maxHeroImageCount
parameter.
Hero images are optimized by server-side rendering the img
element inside the amp-img
element. This can siginificantly improve image rendering performance and reduce the largest contentful paint (LCP) metric from Core Web Vitals.
optimizeHeroImages
[true|false]
true
preloadHeroImage
Deprecated, use optimizeHeroImages instead.
verbose
Enable verbose mode with more detailed logging output.
verbose
[true|false]
false
AMP Optimizer helps you serve optimized images. For this to work, you need to provide a function that maps an image src
to a resized srcset
source value. The image resizing needs to either happen at build time (e.g. for static sites) or via a image hosting service such as thumbor.
Here is an example implementation that appends the image width to the src
:
1const ampOptimizer = AmpOptimizer.create({ 2 // parameters are the amp-img `src` and the `width` of the to be generated srcset source value 3 imageOptimizer: (src, width) => { 4 // we cannot rename if the image does not have a file extension 5 const index = src.lastIndexOf('.'); 6 if (index === -1) { 7 // return null means we won't generate a srcset source value for this width 8 return null; 9 } 10 const prefix = src.substring(0, index); 11 const postfix = src.substring(index, src.length); 12 return `${prefix}.${width}w${postfix}`; 13 }; 14})
Using this implementation, AMP Optimizer will transform the following amp-img
declarations:
1<!-- Injects srcset for responsive layout --> 2<amp-img src="image1.png" width="400" height="800" layout="responsive"></amp-img> 3<!-- Ignores existing srcset --> 4<amp-img 5 layout="fill" 6 srcset="image-1x.png 1x, 7 image-2x.png 2x" 8></amp-img>
into:
1<!-- Injects srcset for responsive layout --> 2<amp-img 3 src="image1.png" 4 width="400" 5 height="800" 6 layout="responsive" 7 srcset="image1.470w.png 470w, image1.820w.png 820w, image1.1440w.png 1440w" 8></amp-img> 9<!-- Ignores existing srcset --> 10<amp-img 11 layout="fill" 12 srcset="image-1x.png 1x, 13 image-2x.png 2x" 14></amp-img>
Important when using layout=responsive
use the width
and height
attribute to specify the minimum image dimensions. For example, for a full-bleed hero image on mobile, specify the width aswidth=320
.
It's possible to pass incomplete documents and AMP Optimizer will add any missing tags and extension imports required by a valid AMP document.
1const originalHtml = ` 2 <h1>Hello World!</h1> 3 <amp-twitter width="375" 4 height="472" 5 layout="responsive" 6 data-tweetid="1182321926473162752"> 7 </amp-twitter> 8`; 9 10// you can pass the canonical URL, default is `.` 11const opts = { 12 canonical: '/example.html', 13}; 14ampOptimizer.transformHtml(originalHtml, opts).then((optimizedHtml) => { 15 // optimizedHtml will be a valid AMP document 16 console.log(optimizedHtml); 17});
AMP Optimizer supports converting Markdown to AMPHTML. A typical conversion flow would be:
README.md => HTML => AMP Optimizer => valid AMP
The AMP Optimizer converts <img>
tags into <amp-img>
or <amp-anim>
tags when in Markdown mode. Enable Markdown mode via markdown : true
. AMP Optimizer will try to resolve image dimensions from the actual files. Images wider than 320px will automatically get an intrinsic
layout.
All other Markdown features are already supported by AMP.
You can pass an additional option imageBasePath
to specify a base path used to resolve an image during build, this can be a file system path or URL prefix.
Important: for image size detection to work, an optional dependency
probe-image-size
needs to be installed via NPM.
1npm install probe-image-size --save-dev
Example:
1const AmpOptimizer = require('@ampproject/toolbox-optimizer'); 2const md = require('markdown-it')({ 3 // don't sanitize html if you want to support AMP components in Markdown 4 html: true, 5}); 6 7// enable markdown mode 8const ampOptimizer = AmpOptimizer.create({ 9 markdown: true, 10}); 11 12const markdown = ` 13# Markdown 🤯 14 15Here is an image declared in Markdown syntax: 16 17. 18 19You can directly declare AMP components: 20 21<amp-twitter width="375" 22 height="472" 23 layout="responsive" 24 data-tweetid="1182321926473162752"> 25</amp-twitter> 26 27Any missing extensions will be automatically imported. 28`; 29 30const html = md.render(markdown); 31 32const amphtml = await ampOptimizer.transformHtml(html, { 33 canonical: filePath, 34});
You can find a working sample here.
AMP Optimizer supports custom HTML transformations:
1const AmpOptimizer = require('@ampproject/toolbox-optimizer'); 2const {createElement, firstChildByTag, appendChild} = AmpOptimizer.NodeUtils; 3 4class CustomTransformer { 5 constructor(config) { 6 this.log_ = config.log.tag('CUSTOM'); 7 } 8 transform(tree, params) { 9 this.log_.info('Running custom transformation for ', params.filePath); 10 const html = firstChildByTag(tree, 'html'); 11 if (!html) return; 12 const head = firstChildByTag(html, 'head'); 13 if (!head) return; 14 const desc = createElement('meta', { 15 name: 'description', 16 content: 'this is just a demo', 17 }); 18 appendChild(head, desc); 19 } 20} 21 22// it's best to run custom transformers first 23const customTransformations = [CustomTransformer, ...AmpOptimizer.TRANSFORMATIONS_AMP_FIRST]; 24 25// pass custom transformers when creating the optimizer 26const optimizer = AmpOptimizer.create({ 27 transformations: customTransformations, 28}); 29// you can add custom parameters on a per document basis 30const transformedHtml = await optimizer.transformHtml(html, { 31 filePath, 32});
Checkout the samples to learn how to customize AMP Optimizer.
The biggest performance gain results from removing the AMP boilerplate code. However, under some circumstances it's not possible to remove the boilerplate code:
amp-experiment
, amp-story
or amp-dynamic-css-classes
components are used (code).To find out, why the AMP boilerplate could not be removed, enable verbose
mode:
1// globally 2const optimizer = ampOptimizer.create({ 3 verbose: true, 4});
... or for individual pages:
1// per transformation
2ampOptimizer.transformHtml(originalHtml, {
3 verbose: true,
4});
Applying the transformations to an AMP file consumes additional server resources. Also, since the entire file is needed to apply the transformations, it also becomes impossible to stream the response while applying it. In order to avoid server overhead, if the set of AMP files to be transformed is known in advance, transformations should be run at build time.
Most websites have a more dynamic nature though and are not able to apply the transformations statically. For such cases it is possible to run the transformations after AMP pages are rendered, e.g. in an Express middleware. In that case, to achieve best performance, it's best to cache transformed pages for subsequent requests. Caching can take place on the CDN level, on the site's internal infrastructure (eg: Memcached), or even on the server itself, if the set of pages is small enough to fit in memory.
AMP Optimizer inlines CSS styles required by AMP. To make sure, that the inlined CSS stays in sync with the latest AMP release, we recommend to re-generate pages at least once a weekOut-of-sync CSS will not break your page, but it could theoretically cause AMP components to briefly appear with the "wrong" styles, such as being visible when they should be hidden. The good news is that these glitches will only be temporary, because as soon as the AMP JS starts, it will check the inlined CSS and update it if required.
Warning: these features are experimental and might result in invalid AMP pages.
When using experimental features resulting in invalid AMP it's best to setup paired AMP mode. Paired AMP mode will add <link rel=amphtml href=${ampUrl}>
to the transformed page, were ampUrl
needs to point to the valid version of this page.
Example:
1const optimizer = AmpOptimizer.create({
2 transformations: AmpOptimizer.TRANSFORMATIONS_PAIRED_AMP,
3});
4const ampFilePath = filePath.substring(1, filePath.length).replace('.html', '.amp.html');
5const transformedHtml = await optimizer.transformHtml(html, {
6 // needed to calculate the `<link rel=amphtml href=${ampUrl}>`
7 ampUrl: ampFilePath,
8});
The ampRuntimeVersion
parameter will rewrite all AMP runtime and extension imports to the specified version. For example:
https://cdn.ampproject.org/v0.js
will be replaced with:
https://cdn.ampproject.org/rtv/001515617716922/v0.js
Versioning the AMP runtime URLs has one main benefit: versioned AMP runtime URLs are served with a longer max-age than the unversioned ones. This means AMP pages served with versioned AMP runtime benefit from better browser caching.
Important: when using versioned AMP runtime URLs make sure to invalidate all caches whenever a new AMP runtime is released. This is to ensure that your AMP pages always use the latest version of the AMP runtime.
You can use @ampproject/toolbox-runtime-version to retrieve the latest version of the AMP runtime. Here is a sample to apply the optimizations including versioning the URLs:
1const ampOptimizer = require('@ampproject/toolbox-optimizer'); 2const ampRuntimeVersion = await runtimeVersion.currentVersion(); 3 4// The input string 5const originalHtml = ` 6<!doctype html> 7<html ⚡> 8... 9`; 10 11// Additional options can be passed as the second argument 12const optimizedHtml = await ampOptimizer.transformHtml(originalHtml, { 13 ampUrl: 'canonical.amp.html', 14 ampRuntimeVersion: ampRuntimeVersion, 15}); 16 17console.log(optimizedHtml);
Add placeholders for amp-img
and amp-video
posters. The placeholders are blurry versions of the corresponding original source. The blur will be displayed as the <amp-img>
is rendering, and will fade out once the element is loaded. The current requirements of appending a blurry placeholder is for the element is to be a JPEG that is either responsive or a poster for an amp-video
.
Important: blurry image placeholder computation is computationally expensive. Make sure to only use it for static or cached pages.
This transformer supports the following options:
blurredPlaceholders
: Enables blurry image placeholder generation. Default is false
.imageBasePath
: specifies a base path used to resolve an image during build.maxBlurredPlaceholders
: Specifies the max number of blurred images. Defaults to 5.blurredPlaceholdersCacheSize
: Specifies the max number of blurred images to be cached
to avoid expensive recalculation. Set to 0 if caching should be disabled. Set to -1 if
all placeholders should be cached (good for static sites). Defaults to 30.Usage:
1const optimizer = AmpOptimizer.create({ 2 blurredPlaceholders: true, 3});
It's possible to rewrite the AMP framework and component imports to a different domain than cdn.ampproject.org
.
Example:
1const ampOptimizer = require('@ampproject/toolbox-optimizer');
2
3// The input string
4const originalHtml = `
5<!doctype html>
6<html ⚡>
7...
8`;
9
10// Additional options can be passed as the second argument
11const optimizedHtml = await ampOptimizer.transformHtml(originalHtml, {
12 ampUrl: 'canonical.amp.html',
13 // this will rewrite https://cdn.ampproject.org/v0.js to /amp/v0.js
14 ampUrlPrefix: '/amp',
15});
16
17console.log(optimizedHtml);
Ideally, when self-hosting the AMP framework, amp-geo-0.1.js
should be patched at delivery time to replace {{AMP_ISO_COUNTRY_HOTPATCH}}
with the ISO 3166-1 alpha-2 country code where the request originated (reference). If your host does not have this capability, you can instead rely on a web API to return the country at runtime. The web API must be secure (HTTPS), adhere to AMP CORS guidelines, and return JSON in the following format:
1{"country": "de"}
where in this example, de
is the ISO 3166-1 alpha-2 country code for Germany.
Example:
1const ampOptimizer = require('@ampproject/toolbox-optimizer');
2
3// The input string
4const originalHtml = `
5<!doctype html>
6<html ⚡>
7...
8`;
9
10const optimizedHtml = await ampOptimizer.transformHtml(originalHtml, {
11 // this will instruct amp-geo to fetch the user's country from an API
12 // which returns JSON in format: {"country": "de"}
13 geoApiUrl: 'https://example.com/geo',
14});
15
16console.log(optimizedHtml);
AMP Optimizer uses a snapshot based testing approach. To execute the tests, run in the project root:
1npm run test:node
Transformer tests are located in:
- spec/transformers/valid/TransformerName/test-name/
expected_output.html
input.html
The transformation input is defined in input.html
, whereas expected_output.html
contains the expected
outcome of the transformation. Don't edit expected_output.html
manually, instead, after changing
a transformer implementation, run:
1npm run test:optimizer:snapshot
to store a new snapshot version in expected_output.html
.
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
Found 13/19 approved changesets -- score normalized to 6
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
98 existing vulnerabilities detected
Details
Score
Last Scanned on 2025-06-30
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn MoreLast Day
-15.4%
3,985
Compared to previous day
Last Week
-10%
50,608
Compared to previous week
Last Month
12%
214,901
Compared to previous month
Last Year
-41.5%
2,225,482
Compared to previous year