
Company News
Socket Named Top Sales Organization by RepVue
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.
create-amp-page
Advanced tools
Full fledged static side generator composed out of extendable gulp tasks, optimized for - but not limited to - AMP.
Fast development of fast pages.
Static site generator built with gulp tasks, using Twig templates, optimized for building AMP pages - but not limited to AMP.
Support for Sass, CSS optimizing, CSS into head injection, media file compressing, automatic resizing of images by srcset, endless copy tasks, Twig global and optional per-page data with JSON and/or frontmatter, browsersync with custom static server middlewares, AMP Optimizer or HTML Minifier (for non-AMP), remove unused CSS (currently only for inline CSS). Different ways to define pages, can be connected with e.g. netlify cms.
🚀 Checkout the starter template repositories!
1. Create a project folder, init your project with npm init
2. Create a Gulpfile.js and paste the following content in it. For all options and docs see the AmpCreatorOptions typing.
import path from 'path'
import gulp from 'gulp'
import {ampCreator, getPageInfo} from 'create-amp-page'
// since `1.0.0-beta.0` the amp-optimizer is a peer-dep (you need to install it additionally!)
// makes only sense if you want AMP-valid HTML
import AmpOptimizer from '@ampproject/toolbox-optimizer'
const port = 4488
/**
* @type {PagesUrlsMap}
*/
const urls = {
example: {
local: {base: 'http://localhost:' + port + '/default/'},
prod: {base: 'https://example.org/'},
},
}
const pages = {
example: {
paths: {
styles: 'src/styles',
stylesInject: 'main.css',
style: 'main.scss',
html: 'src/html',
dist: 'build',
distStyles: 'styles',
},
},
}
const isDev = process.env.NODE_ENV === 'development'
const tasks = ampCreator({
port: port,
dist: 'build',
srcMedia: 'src/media',
distMedia: 'media',
pages: pages,
collections: [{
fm: (file) => 'src/data/' + path.basename(file).slice(0, '.twig'.length * -1) + '.md',
tpl: 'src/html/pages/*.twig',
base: '',
pageId: 'example',
}, {
fm: 'src/data/blog/*.md',
tpl: 'src/html/blog.twig',
base: 'blog',
pageId: 'example',
}],
// when `ampEnabled: true` use the `ampOptimizer` for HTML minification and more
ampOptimizer: !isDev ? AmpOptimizer.create({}) : undefined,
// when `ampEnabled: false` use `minifyHtml` for HTML minification and more
// minifyHtml: false,
cleanInlineCSS: !isDev,
cleanInlineCSSWhitelist: ['#anc-*'],
// for css injection of non-AMP pages:
// cssInjectTag: '<style>',
twig: {
data: {ampEnabled: true},
fmMap: (data, files) => {
const pageId = files.pageId
const {
pagePath, pageBase,
} = getPageInfo(files, urls, pageId, isDev ? 'local' : 'prod')
const pageData = pages[pageId]
return {
pageId: pageId,
styleSheets: [
pageData.paths.stylesInject,
],
head: {
title: data.attributes.title,
description: data.attributes.description,
lang: data.attributes.lang,
},
links: {
canonical: pageBase + pagePath,
origin: pageBase,
cdn: isDev ? 'http://localhost:' + port + '/' : pageBase,
},
content: data.body,
}
},
logicLoader: async () => {
return {}
},
},
prettyUrlExtensions: ['html'],
})
Object.keys(tasks).forEach(taskName => gulp.task(taskName, tasks[taskName]))
3. Add those scripts into package.json, the project must be type=module:
{
"type": "module",
"scripts": {
"tasks": "gulp --tasks",
"start": "cross-env NODE_ENV=development gulp watch",
"build": "cross-env NODE_ENV=production gulp build",
"clean": "gulp clean"
}
}
4. Create a postcss.config.js with:
module.exports = {
plugins: [
require('cssnano')({
preset: ['default', {
discardComments: {
removeAll: true,
},
}],
}),
],
}
5. Add your src folders & files, minimum for this config: src/styles/main.scss, src/html/pages/index.twig, src/data/index.md and src/media/.gitkeep
6. Install this SSR: npm i --save create-amp-page
7. Run npm start and happy coding!
Checkout the starter repos:
Two integrated ways of page generation:
Since 1.0.0-alpha.8 both page generations are configured by collections:
const options = {
collections: [
{
// create one page per `twig` file, `fm` needs to return the relative path to the frontmatter file (or `undefined` fo no-fm`
fm: (file) => 'example/data/' + path.basename(file).slice(0, '.twig'.length * -1) + '.md',
tpl: 'example/html/pages/*.twig',
base: '',
pageId: 'example',
},
{
// create one page per `fm` file, one `tpl` is used for all pages
fm: 'example/data/blog/*.md',
tpl: 'example/html/blog.twig',
base: 'blog',
pageId: 'example',
}
],
}
Get metadata and sizing for image, caches the read-result for each execution, purging cache on each watch trigger of html.
src is the relative path to media folder incl. media foldersrcset is an array of objects, define in which image sizes the image should be resized
w = width in pixels, internally it calculates the other value proportionalsrc path to filewidth of fileheight of filehash sha1 hash of file contentTemplate using getImage(src, srcset) to fetch metadata and resize images when needed:
{% set image = getImage(src, srcset) %}
<amp-img
src="{{ image.src ~ '?key=' ~ (image.hash|slice(0,12)) }}"
width="{{ image.width }}"
height="{{ image.height }}"
{# generate srcset with same syntax like `getImage` #}
srcset="{% for set in srcset %}{{ addImageSuffix(image.src, '_'~set.w~'w') ~ '?key=' ~ (image.hash|slice(0,12))~' '~set.w~'w' }}{% if loop.index < (srcset|length) %}, {% endif %}{% endfor %}"
sizes="{{ sizes }}"
layout="responsive"
></amp-img>
Embed then in file, pixels at srcset:
{% embed 'image.twig' with {
src: '/media/img-01.png',
alt: 'A blog hero image',
classes: 'flex',
srcset: [
{w: '320'},
{w: '680'},
{w: '920'}
],
sizes: '(max-width: 320px) 320px, (max-width: 600px) 680px',
} %}
{% endembed %}
Generates HTML like:
<amp-img
src="/media/img-01.png?key=2l8ybbe1tjSP"
width="1280" height="421"
srcset="/media/img-01_320w.png?key=2l8ybbe1tjSP 320w, /media/img-01_680w.png?key=2l8ybbe1tjSP 680w, /media/img-01_920w.png?key=2l8ybbe1tjSP 920w"
sizes="(max-width: 320px) 320px, (max-width: 600px) 680px"
layout="responsive"
></amp-img>
Add an image suffix between name and extension:
{{ addImageSuffix(image.src, '_suffix') }}
To embed e.g. css or js files directly in build template, uses the src relative to configured dist:
{{ embedScript('js/main.js') }}
This project is free software distributed under the MIT License.
See: LICENSE.
© 2022 Michael Becker
See github release notes for updates, especially incompatibilities, for features check the current AmpCreatorOptions typing.
This project adheres to semver.
By committing your code/creating a pull request to this repository you agree to release the code under the MIT License attached to the repository.
FAQs
Full fledged static side generator composed out of extendable gulp tasks, optimized for - but not limited to - AMP.
We found that create-amp-page demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.