When to use
- Spin up a disposable WordPress to test a plugin/theme without full stack setup.
- Run or iterate on Playground Blueprints (JSON) locally.
- Build a reproducible snapshot of a site for sharing or CI.
- Switch WP/PHP versions quickly to reproduce issues.
- Debug plugin/theme code with Xdebug in an isolated Playground.
- Run PHPUnit tests without a local database (no Docker, no MySQL).
- Write E2E Playwright tests against a real WordPress instance.
- Automate Playground in CI/CD pipelines via the programmatic
runCLIAPI.
Inputs required
- Host machine readiness: Node.js ≥ 20.18,
npm/npxavailable. - Project path to mount (
--auto-mountor explicit mount mapping). - Desired WP version/PHP version (optional; defaults to latest WP, PHP 8.3).
- Blueprint location/URL if running a blueprint.
- Port preference if 9400 conflicts.
- Whether Xdebug is needed.
Procedure
0) Guardrails
- Playground instances are ephemeral and SQLite-backed; never point at production data.
- Confirm Node ≥ 20.18 (
node -v) before running CLI. - If mounting local code, ensure it is clean of secrets; Playground copies files into an in-memory FS.
1) Quick local spin-up (auto-mount)
cd <plugin-or-theme-root>
npx @wp-playground/cli@latest server --auto-mount
- Opens on http://localhost:9400 by default. Auto-detects plugin/theme and installs it.
- Add
--wp=<version>/--php=<version>as needed. - For classic full installs already present, add
--skip-wordpress-setupand mount the whole tree.
2) Manual mounts or multiple mounts
- Use
--mount=/host/path:/vfs/path(repeatable) when auto-mount is insufficient (multi-plugin, mu-plugins, custom content). - Mount before install with
--mount-before-installfor bootstrapping installer flows. - Reference:
references/cli-commands.md
3) Run a Blueprint (no server needed)
npx @wp-playground/cli@latest run-blueprint --blueprint=<file-or-url>
- Use for scripted setup/CI validation. Supports remote URLs and local files.
- Allow bundled assets in local blueprints with
--blueprint-may-read-adjacent-fileswhen required. - See
references/blueprints.mdfor structure and common flags.
4) Build a snapshot for sharing
npx @wp-playground/cli@latest build-snapshot --blueprint=<file> --outfile=./site.zip
- Produces a ZIP you can load in Playground or attach to bug reports.
5) Debugging with Xdebug
- Start with
--xdebug(or--enable-xdebugdepending on CLI release) to expose an IDE key, then connect VS Code/PhpStorm to the host/port shown in CLI output. - Combine with
--auto-mountfor plugin/theme debugging. - Checklist:
references/debugging.md
6) Version switching
- Use
--wp=to pin WP (e.g., 6.9.0) and--php=to test compatibility. - If feature depends on Gutenberg trunk, prefer the latest WP release plus plugin if available; Playground images track stable WP plus bundled Gutenberg.
7) Browser-only workflows (no CLI)
- Launch quick previews with URL fragments or query params:
- Fragment:
https://playground.wordpress.net/#<base64-or-json-blueprint> - Query:
https://playground.wordpress.net/?blueprint-url=<public-url-or-zip>
- Fragment:
- Use the live Blueprint Editor (playground.wordpress.net) to author blueprints with schema help; paste JSON and copy a shareable link.
8) PHPUnit testing (no database required)
Run PHPUnit inside Playground — every run starts with a clean WordPress install, fully isolated.
Plugin:
npx @wp-playground/cli@latest php \
--auto-mount \
-- \
/wordpress/wp-content/plugins/MY_PLUGIN/vendor/bin/phpunit \
-c /wordpress/wp-content/plugins/MY_PLUGIN/phpunit.xml.dist
Theme (replace plugins/ with themes/):
npx @wp-playground/cli@latest php \
--auto-mount \
-- \
/wordpress/wp-content/themes/MY_THEME/vendor/bin/phpunit \
-c /wordpress/wp-content/themes/MY_THEME/phpunit.xml.dist
- Requires PHPUnit installed via Composer in the project (
composer require --dev phpunit/phpunit). --auto-mountmaps the current directory to the correct WP content path automatically.- Use
--php=8.1 --wp=6.5flags to test against specific versions (PHP 7.4–8.5 supported). - Explicit mount:
--mount=.:/wordpress/wp-content/plugins/MY_PLUGIN.
9) E2E testing with Playwright
Use runCLI from @wp-playground/cli to start a Playground server programmatically in Playwright tests. No Docker, no database.
Install:
npm install --save-dev @playwright/test @wp-playground/cli
npx playwright install chromium
Minimal playwright.config.ts:
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e',
fullyParallel: false,
workers: 1, // prevent port conflicts
timeout: 120_000, // WP boot takes time
expect: { timeout: 30_000 },
use: { screenshot: 'only-on-failure', trace: 'on-first-retry' },
});
First test (tests/e2e/plugin.spec.ts):
import { test, expect } from '@playwright/test';
import { runCLI } from '@wp-playground/cli';
let cli: Awaited<ReturnType<typeof runCLI>>;
test.beforeAll(async () => {
cli = await runCLI({
command: 'server',
blueprint: {
preferredVersions: { php: '8.3', wp: 'latest' },
login: true,
},
});
});
test.afterAll(async () => { await cli?.server?.close(); });
test('dashboard loads', async ({ page }) => {
await page.goto(`${cli.serverUrl}/wp-admin/`);
await expect(page.locator('#wpbody-content')).toBeVisible();
});
Mount a local plugin:
cli = await runCLI({
command: 'server',
mount: { './': '/wordpress/wp-content/plugins/my-plugin' },
blueprint: {
preferredVersions: { php: '8.3', wp: 'latest' },
login: true,
steps: [{ step: 'activatePlugin', pluginPath: 'my-plugin/my-plugin.php' }],
},
});
Locator priority (most to least preferred):
page.getByRole()— buttons, headings, links, inputspage.getByLabel()— labeled form fieldspage.getByText()— visible textpage.getByTestId()— your owndata-testidattributespage.locator()— CSS/XPath last resort (WP core layout elements like#wpadminbar)
Version matrix pattern:
const matrix = [{ php: '8.1', wp: '6.5' }, { php: '8.3', wp: 'latest' }];
for (const { php, wp } of matrix) {
test.describe(`PHP ${php} + WP ${wp}`, () => {
// ... beforeAll/afterAll with those versions
});
}
CI (GitHub Actions): see references/e2e-playwright.md.
10) Programmatic runCLI API (scripts / Vitest integration)
import { runCLI } from '@wp-playground/cli';
const server = await runCLI({
command: 'server',
php: '8.3',
wp: 'latest',
login: true,
mount: [{ hostPath: './my-plugin', vfsPath: '/wordpress/wp-content/plugins/my-plugin' }],
blueprint: { steps: [{ step: 'activatePlugin', pluginPath: 'my-plugin/plugin.php' }] },
});
// server.serverUrl — the base URL
// server.server — the HTTP server instance (call .close() in afterEach)
// server[Symbol.asyncDispose]() — cleanup in Vitest afterEach
Use wordpressInstallMode: 'do-not-attempt-installing' + skipSqliteSetup: true when testing pure PHP with no WordPress overhead.
Verification
- Verify mounted code is active (plugin listed/active; theme selected).
- For blueprints/snapshots, re-run with
--verbosity=debugto confirm steps executed. - Run targeted smoke (e.g.,
wp plugin listinside Playground shell via browser terminal if exposed) or UI click-path.
Failure modes / debugging
- CLI exits complaining about Node: upgrade to ≥ 20.18.
- Mount not applied: check path, use absolute path, add
--verbosity=debug. - Blueprint cannot read local assets: add
--blueprint-may-read-adjacent-files. - Port already used:
--port=<free-port>. - Slow/locked UI: disable
--experimental-multi-workerif enabled; or enable it to improve throughput on CPU-bound runs.
Escalation
- If PHP extensions or native DB access are required, Playground may be unsuitable; fall back to full WP stack or wp-env/Docker.
- For browser-only embedding or VS Code extension specifics, consult the upstream docs: https://wordpress.github.io/wordpress-playground/