Self-hosted service
GoogleChromeLabs/ProgressiveWordPress avatar
GoogleChromeLabs/ProgressiveWordPress

ProgressiveWordPress shows what a service worker theme can do to WordPress

A Sample WordPress-based Progressive Web App

559 stars93 forksJavaScriptNOASSERTION

At a glance

What is it?
GoogleChromeLabs' ProgressiveWordPress is a sample WordPress theme that ships as a progressive web app, with a service worker, web components, an offline plugin and a three-container Docker setup for local work. It is a demonstration rather than a maintained theme, pinned to a 2017 era stack, and the value is in the pattern it shows.
Who is it for?
Adopt ProgressiveWordPress as a reference if you are a WordPress developer who wants to see what a service worker theme, a lazy image plugin and a web component approach look like inside a real site, since the code is short enough to read in an afternoon. Do not adopt it as a production theme or as a starting point for a new site, because the stack is pinned to 2017 era tooling and the underlying WordPress image is version 4.8.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A theme, a service worker and a plugin that ships with it

ProgressiveWordPress is a sample WordPress-based progressive web app, and it is worth being precise about what is in the repository, because the name suggests a plugin and the structure suggests a theme.

The top level of the repository has a src directory, a build.js, an async_array.js, a package.json, a Dockerfile, a docker-compose file, a caddy directory and a design directory. The compose file mounts the built theme into the WordPress container at the themes path, under the name surmblog_dev, and it mounts a second path as a plugin, pwp-lazy-image-plugin. So the build output is both a theme and a plugin, and both are mounted into the container at run time rather than baked into the image.

That arrangement is the interesting part. It means the Docker container runs a stock WordPress and your changes appear through a volume mount, so the edit and refresh cycle does not require rebuilding the image.

The plugin name gives away one of the techniques in the sample, a lazy image plugin that only loads images when they are needed, which is the kind of thing a service worker plus a custom element can do that a normal WordPress plugin cannot.

The theme build is npm install, npm run build, npm run watch

The local development path for the theme is three commands, and it needs Node 8 or newer.

bash
$ npm install
bash
$ npm run build
bash
$ npm run watch

The build script in the package manifest is more than a bundler call. The build entry is npm version --no-git-tag-version -f patch followed by mkdir -p dist and node build.js, so every build bumps the patch version of the package. There is also a copy-static step that copies the PHP files from src into dist, and a minify-css step that runs csso over the CSS files.

The watch mode is configured in the manifest rather than in a separate file, with patterns covering src and build.js, and extensions of php, json, js, html and css. So the continuous build watches for changes across the whole set of file types the theme is written in, including the PHP, which tells you the theme is a hybrid: some of the work is server side and some is client side.

The dependency list is the clearest evidence of the era and the technique. SystemJS is there for module loading, Babel with a transform plugin for ES2015 modules, @webcomponents/custom-elements for the custom element support, and babili and cssnano for minification. There is no webpack or rollup in sight, which was a deliberate choice for a sample that has to be understandable rather than clever.

Three containers, and HTTP/2 on a second port

The Docker setup is a network of Caddy, Apache2 with PHP7 and MySQL. The Apache and PHP container is based on the official WordPress Docker image, and it is also published to Docker Hub under surma/progressivewordpress if you would rather not build it locally. WordPress needs a MySQL server, and the official MySQL image is used for that. Caddy is added to the mix for easy local HTTP/2 development.

The compose file names the services mysql, pwp and caddy. MySQL runs the mysql:8.0 image with the root password taken from the DB_PASSWORD environment variable and its data kept in a local directory under tmp. The pwp service builds from the repository root and runs the surma/progressivewordpress image, with the theme and plugin volumes described above plus a directory for uploads. The caddy service builds from its own directory in the repository and publishes two ports, both bound to 127.0.0.1, one for 8080 and one for 8443.

The start commands are:

bash
$ docker pull surma/progressivewordpress # if you don’t want to build locally
$ docker-compose up -d

The WordPress install wizard is then available at http://localhost:8080. If you want HTTP/2 during development there are three extra steps: point surmblog.dev to 127.0.0.1 using /etc/hosts, trust the certificate in the caddy/certs directory of the repository, and go to https://surmblog.dev:8443 instead. The certificate being committed to the repository is the practical consequence of that last route.

The Dockerfile patches WordPress and Apache by hand

The image build does something that is unusual enough to be instructive. It starts from wordpress:4.8-php7.0-apache, which dates the sample precisely, and then it edits the container filesystem in two places.

The first edit fixes HTTPS behind a proxy. It appends a conditional to the WordPress configuration sample that sets the HTTPS server variable when the forwarded protocol header says https, because the sample has to work behind Caddy. The second edit fixes GZIP. The Apache configuration has the closing VirtualHost tag removed, several directives inserted, and the tag appended again, and the headers module is symlinked into mods-enabled. The inserted directives set a no-cache default caching strategy to avoid what the comment calls heuristics stupidity, rewrite the ETag so it matches the gzipped content, unset the If-Modified-Since request header, and close the virtual host block.

Every one of those is a problem you hit when you put a caching reverse proxy in front of WordPress. The fact that they are solved with sed and echo in a Dockerfile tells you the age of the approach and also why the sample is still worth reading, because the problems themselves have not gone away.

The copyright header on the file is 2017 Google Inc, licensed under the Apache License Version 2.0, which matches the licence field in the package manifest. The repository itself carries a LICENSE file that the platform cannot classify as one of the standard licences, so the two are not necessarily the same thing.

Two settings have to be right before the app behaves

The documentation ends the local development section with two admin settings, and they are the kind of detail that will cost an hour if you skip them.

Once the install is done, you have to enable the theme called surmblog in the WordPress admin, at the themes page. And you have to set the permalink style to Post name, at the permalink options page. The service worker and the routing that a progressive web app needs both depend on the URL structure, and the default WordPress permalink style will not produce the URLs the sample expects.

That is a genuine operational detail of the design rather than an oversight in the documentation. A service worker has to be served from a scope that covers the routes the application uses, and a query string based permalink style gives the service worker a different scope from a path based one. It also tells you something about the theme, that it takes over routing to an unusual degree, which is what a WordPress-based progressive web app usually means in practice.

The repository also has a .env file, a docker-compose.prod.yaml alongside the development compose file, a CONTRIBUTING file, a CONTRIBUTORS file, and a design directory, so the sample covers more than the development setup.

A last push in April, and a version number that says it all

The maintenance record is where this sample stops looking current. The last push was on 2026-04-09, and the repository publishes no GitHub releases.

The package manifest version is 0.0.3411, and that number is not arbitrary. The build script runs npm version with the patch flag on every build, so the patch component counts builds. A version of 0.0.3411 means more than three thousand builds have run through that pipeline, which is a good indicator of how much iteration the sample went through.

The manifest also carries a description matching the project tagline, a main entry of index.js, and an author field naming Surma with the surma.link address. The repository URL in the manifest points at the older GoogleChrome organisation path, while the repository itself now lives under GoogleChromeLabs, which is a small trace of the project's history.

The stack underneath is dated in a way that matters if you plan to build on it. Babel core 6, cssnano 3, systemjs 0.20 and a 2017 WordPress base image are all still installable, but the toolchain has moved on, and the Node 8 or newer requirement in the documentation is now a floor rather than a target. The honest way to treat this repository is as a documented example of a technique, not as a maintained theme.

Editorial conclusion

Adopt ProgressiveWordPress as a reference if you are a WordPress developer who wants to see what a service worker theme, a lazy image plugin and a web component approach look like inside a real site, since the code is short enough to read in an afternoon. Do not adopt it as a production theme or as a starting point for a new site, because the stack is pinned to 2017 era tooling and the underlying WordPress image is version 4.8. Before you try it, check three things: that your Docker and Node tooling can still build it, that you know the theme must be enabled and the permalink style set to Post name before the app behaves, and that the licence file at the top of the repository is a custom one even though the package manifest records Apache-2.0.

Frequently asked questions

How do I run ProgressiveWordPress locally?

Use Docker. The setup is a network of Caddy, Apache2 with PHP7 and MySQL, and you can pull the published image with docker pull surma/progressivewordpress or run docker-compose up -d to build it yourself. The WordPress install wizard is then at http://localhost:8080.

How do I build the ProgressiveWordPress theme?

With Node 8 or newer, run npm install and then npm run build, or npm run watch for continuous builds. The build bumps the package patch version, copies the PHP files from src into dist and minifies the CSS, and the watch mode covers php, json, js, html and css.

What do I have to change in WordPress for the Progressive Word App to work?

Enable the theme called surmblog in the theme admin page, and set the permalink style to Post name in the permalink options. The sample also supports HTTP/2 locally by pointing surmblog.dev at 127.0.0.1 in /etc/hosts, trusting the certificate in the caddy/certs directory, and using https://surmblog.dev:8443.

Which Webpack does ProgressiveWordPress use?

It does not use one. The dependencies list SystemJS for module loading, Babel with an ES2015 modules transform plugin, @webcomponents/custom-elements, and babili and cssnano for minification. The build itself is a node build.js script plus a copy of the PHP files from src into dist.

What licence is ProgressiveWordPress under?

The package manifest records Apache-2.0 and the Dockerfile carries an Apache 2.0 copyright header, but the repository also has a LICENSE file that the hosting platform cannot classify as a standard licence, so read that file directly. The last push was on 2026-04-09 and there are no GitHub releases.

Official sources

  1. GoogleChromeLabs/ProgressiveWordPress on GitHub
  2. Issues
  3. Project website
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/googlechromelabs-progressivewordpress.svg)](https://hysenlabs.com/projects/googlechromelabs-progressivewordpress)

Was this useful?