Client-side WebAssembly WordPress with no server

This early demo runs a full WordPress directly in the browser without a PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher server! While it isn’t fully stable yet, it is a major breakthrough that could transform learning, contributing, and using WordPress. This post explores the opportunities and explains in detail how it works. 

Your help is needed to realize the vision laid out below. None of the presented mockups and early explorations are currently reliably implemented. This project needs volunteers to stabilize the code and build revolutionary tools on top of it. If you’d like to be a part of it, please say so in the comments!

Learning WordPress in the browser

The code examples in the WordPress handbook could become runeditable, like in this early preview:

Furthermore, an in-browser IDEIDE Integrated Development Environment. A software package that provides a full suite of functionality to software developers/programmers. Normally an IDE includes a source code editor, code-build tools and debugging functionality. could lead new contributors through solving their first “Good first issue” without setting up a local development environment. Just like this early preview:

Finally, a guided code editor could become a primary teaching tool for new developers. They would click a “Build your first blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience.” button on The community site where WordPress code is created and shared by the users. This is where you can download the source code for WordPress core, plugins and themes as well as the central location for community conversations and organization. and immediately start coding. This is what it could look like:

WordPress Developer tools

Testing code on different WordPress, PHP, and GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. versions currently require a tedious setup. With an in-browser WordPress IDE, it wouldn’t require any setup at all. As a developer, you would switch between the different versions by selecting different entries in a box:

Taking it further, the continuous integration pipeline could replay the failed tests right in the browser and provide a code editor to debug and fix the problem on the spot:

On a different note, the desktop and mobile apps could reuse WordPress code by running an actual WordPress instance – even when offline.

Finally, WordPress could potentially be scaled up by spinning up many tiny self-contained WASM instances directly on the edge servers.


Embedding a demo of your pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the Plugin Directory or can be cost-based plugin from a third-party, pattern, or theme directly on the website makes another great use case. One such demo lives on, an in-browser WordPress readme.txt editor. Here’s what the plugin directory could look like:

Today, a WordPress server is required, and ideally, one WordPress per user to always present the same initial state.

The in-browser WordPress enables one private WordPress per user with no marginal server cost. Everyone can start logged-in as an adminadmin (and super admin) with no security risks.

Furthermore, importing an existing WordPress website into WASM runtime would create a staging website. Users could try themes and plugins on a copy of their site without affecting the live sites. Then, once the staging website looks good, they’d click a button and publish the changes.

How else would you use the in-browser WordPress? Please share your ideas in the comments so more use cases can be considered in the future.

How does the in-browser WordPress work?

The code lives in the GitHub repository.

In short:

  • PHP is compiled to WASM with Emscripten
  • WordPress is packaged into a data bundle
  • A service worker traps HTTPHTTP HTTP is an acronym for Hyper Text Transfer Protocol. HTTP is the underlying protocol used by the World Wide Web and this protocol defines how messages are formatted and transmitted, and what actions Web servers and browsers should take in response to various commands. requests and re-routes them to WordPress.

See more details below.

PHP is compiled to WASM with Emscripten

Firstly, PHP is compiled using an adjusted recipe from the php-wasm repo. It’s powered by Emscripten, a drop-in replacement for the C compiler. Unfortunately, MySQLMySQL MySQL is a relational database management system. A database is a structured collection of data where content, configuration and other options are stored. currently cannot run as WASM. However, SQLite can, and WordPress supports SQLite via the wp-db-sqlite plugin.

Emscripten compilation yields two files: webworker-php.wasm, which is the assembly, and webworker-php.js, which downloads the assembly file, creates a virtual heap, and exposes named native functions conveniently wrapped to accept and return JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. data types.

You need to load the webworker-php.js in a webworker and add a tiny wrapper class:

const php = new PHPWrapper();
    await`<?php echo "Hello world";`).stdout
// "Hello world"

See the Dockerfile, the compilation script, and the php-wasm playground.

WordPress is packaged into a data bundle

WebAssembly PHP runtime has its own filesystem and WordPress is shoehorned into it as a data bundle.

First, a fresh WordPress distribution is downloaded and patched with the wp-db-sqlite plugin. It’s about 66MB large, but an optimization pipeline makes that 46MB by minifying the PHP files and removing non-essential static assets. Getting down to just 12MB is possible, but it’s not easy.

Second, the WordPress installation process kicks in. A script serves our WordPress via the built-in PHP server and sends a special curl request to /wp-admin/install.php?step=2. Unfortunately, wp-cliWP-CLI WP-CLI is the Command Line Interface for WordPress, used to do administrative and development tasks in a programmatic way. The project page is is hard-wired to MySQL and not a good match here.

Lastly, Emscripten’s file_packager turns our WordPress copy into a data file named and a JavaScript loader named wp.js. Loading both the PHP runtime from webworker-php.js and the WordPress data bundle from wp.js allows you to run <?php require “wordpress/index.php”; right in the browser!

A service worker reroutes the HTTP to WordPress

WordPress reads request information from $_SERVER, $_GET, $_COOKIE and so on. Normally these variables are populated by ApacheApache Apache is the most widely used web server software. Developed and maintained by Apache Software Foundation. Apache is an Open Source software available for free., NginxNGINX NGINX is open source software for web serving, reverse proxying, caching, load balancing, media streaming, and more. It started out as a web server designed for maximum performance and stability. In addition to its HTTP server capabilities, NGINX can also function as a proxy server for email (IMAP, POP3, and SMTP) and a reverse proxy and load balancer for HTTP, TCP, and UDP servers., or another web server. However, in this case, there isn’t one.

Instead, the superglobal variables are populated “manually”:

function request(path, method, body, cookies) {
        $_POST   = json_parse('${JSON.stringify(body)}');
        $_COOKIE = json_parse('${JSON.stringify(cookies)}');
        $_SERVER['REQUEST_URI'] 	= $path;
        $_SERVER['REQUEST_METHOD']  = "${method}";
        require "wordpress/index.php";

That takes care of the request, but capturing the complete HTTP response is still necessary. The PHP outputs information in only two ways: stdout and stderr. The response body comes out via stdout, but the HTTP status code and headers don’t. They need to be manually streamed to stderr where the JavaScript app can capture them:

register_shutdown_function(function() use() {
    $stdErr = fopen('php://stderr', 'w');
    fwrite($stdErr, json_encode(['status_code', http_response_code()]) . "\n");
    fwrite($stdErr, json_encode(['session_id', session_id()]) . "\n");
    fwrite($stdErr, json_encode(['headers', headers_list()]) . "\n");

The final request handler is similar to this:

function request(path, method, body, cookies) {
    const { stdout, stderr, exitCode } =`<?php
        $_POST   = json_parse('${JSON.stringify(body)}');
        // ... Populate other superglobals...
        register_shutdown_function(function() use() {
            $stdErr = fopen('php://stderr', 'w');
            fwrite($stdErr, json_encode(['status_code', http_response_code()]) . "\n");
            // Output other information
        require "wordpress/index.php";
    const { statusCode, headers, body } =
        rawOutputToResponse({ stdout, stderr, exitCode });
    return { statusCode, headers, body };

The actual code is much more involved, but it’s based on the same idea.

Node.js is supported, too

Running WordPress in different JSJS JavaScript, a web scripting language typically executed in the browser. Often used for advanced user interfaces and behaviors. runtimes is a matter of connecting these major building blocks with runtime-specific plumbing. Today there is an in-browser version and a node.js version. Here’s how they differ:

In-browser version

WordPress is rendered in an iframeiframe iFrame is an acronym for an inline frame. An iFrame is used inside a webpage to load another HTML document and render it. This HTML document may also contain JavaScript and/or CSS which is loaded at the time when iframe tag is parsed by the user’s browser. by a minimal index.htmlHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. once both workers are loaded.

Node.js version

  • Emscripten compiles PHP for the Node.js runtime.
  • A Node.js script loads the WebAssembly PHP and mounts a local WordPress directory inside the PHP filesystem.
  • The same Node.js script starts a web server. It serves most requests as static files, except the .php ones, which it routes to WordPress.

WordPress is served on localhost:8888.

Running an in-browser WordPress IDE with Stackblitz

Stackblitz is the in-browser IDE that makes Svelte docs interactive:

It could do the same for WordPress docs; see this early preview.

Stackblitz doesn’t need a specialized backend. It runs Node.js, npm, and webpack right in the browser via WebAssembly. Other than hosting and delivering a few megabytes of data, Stackblitz has no marginal costs. This idea is called Web Containers.

Learning WordPress and writing code used to be separated. Now they can be one and the same. From runnable code snippets to new, svelte-like docs formats, WebContainers + WebAssembly WordPress is an educational game-changer.

WordPress runs in Stackblitz via a Node.js server

The minimal WordPress plugin dev setup on Stackblitz consists of:

  • A node.js WordPress server (via an npm package)
  • A plugin directory mounted inside of mu-plugins
  • A wp-scripts setup to watch and bundle the JavaScript files

@gziolo went a step further and prepared a version with an editable Gutenberg block generated via create-block. All it took was running the npx @wordpress/create-block example-static command as shown in the create-block documentation, and restarting the dev server with npm run start:

However, a Node.js WordPress server on Stackblitz is sluggish

Rendering a single wp-admin page takes a second locally but up to 40 seconds on Stackblitz. That’s much longer than most developers are willing to wait. Here’s why that happens:

A local Node.js WordPress server works like this:

  • WordPress runs on WebAssembly PHP
  • WebAssembly PHP runs on a native Node.js

However, a Stackblitz Node.js WordPress server has an extra layer:

  • WordPress runs on WebAssembly PHP
  • WebAssembly PHP runs on WebAssembly Node.js
  • WebAssembly Node.js runs on a native Chrome

It’s like a box in a box: A WebAssembly runtime in Chrome runs Node.js that has its own WebAssembly runtime. WordPress runs on the latter, not on the former, and that indirection causes a massive slowdown.

Luckily, the browser can run WebAssembly natively without an intermediate Node.js layer.

An in-browser WASM WordPress is really fast

Here’s a Stackblitz WordPress setup using the webworker WordPress version:

It is much faster! Unfortunately, the speed comes at the expense of simplicity.

Stackblitz requires a service worker just like the in-browser WASM WordPress. However, you can only have one per domain. Therefore, the WordPress files in this example are hosted on a Netlify domain.

Then, the Stackblitz files can’t be directly mounted into the in-browser PHP filesystem. The changes need to be synchronized manually. In this example, a node.js file watcher notifies the PHP webworker about the updates via WebSockets and onmessage/postMessage. There are bugs, but all are fixable.

Known issues

WASM WordPress issues:

  • The in-browser WordPress builds sometimes crash the Chrome tab, see the GitHub issue. Firefox, Safari, and node.js are much more stable.

Stackblitz integration issues:

  • The in-browser Stackblitz setup doesn’t watch subdirectories.
  • WordPress exceeds Stackblitz size limits unless imported as a node_module.
  • Stackblitz corrupts a predefined binary .sqlite database file. A base64-encoded version is shipped instead.
  • Stackblitz doesn’t pass a server-side set-cookie headerHeader The header of your site is typically the first thing people will experience. The masthead or header art located across the top of your page is part of the look and feel of your website. It can influence a visitor’s opinion about your content and you/ your organization’s brand. It may also look different on different screen sizes. to the browser. That’s a bugbug A bug is an error or unexpected result. Performance improvements, code optimization, and are considered enhancements, not defects. After feature freeze, only bugs are dealt with, with regressions (adverse changes from the previous version) being the highest priority.. Workaround: the express.js server handles the cookies internally.

Next steps

Your help is needed to fully realize the vision laid above. None of the presented mockups and early explorations is currently reliably implemented. This project needs contributors to stabilize the code and build revolutionary tools on top of it – see the issues list on GitHub. If you’d like to be a part of it, please say so in the comments!


Props to @bph, @annezazu, and @gziolo for their thorough help with reviewing and editing this post.

#core, #editor, #proposal