WordPress Playground is often used as a one-off browser sandbox: open a link, test a 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 WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party., close the tab.
That is useful, but plugin and theme development usually involves more than one sandbox. You may need a clean site for reproduction, a saved site for ongoing debugging, another site pinned to a different PHPPHP PHP (recursive acronym for PHP: Hypertext Preprocessor) is a widely-used open source general-purpose scripting language that is especially suited for web development and can be embedded into HTML. https://www.php.net/manual/en/index.php version, and a way to inspect all of them without clicking through the interface.
Recent Playground updates make those workflows easier to automate. The Playground website now makes a site management object available to 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.
https://www.javascript.com running on its top-level page. Code in the browser console or a browser automation tool can access it through window.playgroundSites to list and switch sites, save temporary sites, rename saved sites, change runtime settings, or get the active site’s PlaygroundClient.
This post shows how plugin and theme developers can use that APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. from the browser console to speed up real workflows.
One interface for site management
Before these updates, anything outside the Playground UIUI UI is an acronym for User Interface - the layout of the page the user interacts with. Think ‘how are they doing that’ and less about what they are doing. had to recreate site management behavior itself. MCP tools, browser-native WebMCP, DevTools experiments, and UI components all needed some way to list sites, save them, rename them, or switch the active site.
The new PlaygroundSitesAPI gives those workflows one shared interface. After the Playground website loads, JavaScript running on the page can call that interface through window.playgroundSites. This makes site management scriptable without relying on brittle UI automation.
In practical terms, you can now open DevTools and run:
window.playgroundSites.list();
The result is an array of site records:
[
{
slug: "quiet-river",
name: "Quiet River",
storage: "temporary",
isActive: true,
},
];
The storage field tells you whether the site is temporary, saved in browser storage, or saved to the local filesystem. Temporary sites disappear on reload unless you save them first.
The API at a glance
The workflows in this post use these coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. methods:
| Method | What it does |
|---|---|
list() | Lists all known Playground sites and marks the active one. |
getClient() | Returns the PlaygroundClient for the active site, if it has booted. |
isReady() | Resolves when the active site has booted and its client is ready. |
rename(newName) | Renames the active saved site. Temporary sites must be saved first. |
saveInBrowser(name?) | Saves the active temporary site to browser storage. |
saveToLocalFileSystem(name?, handle?) | Saves the active temporary site to a local directory. |
setPhpVersion(version) | Changes the PHP version for the active saved site. |
setNetworking(enabled) | Enables or disables networking for the active saved site. |
delete(siteSlug) | Deletes a saved site by slug. Temporary sites cannot be deleted this way. |
setActiveSite(siteSlug) | Switches to another site and waits for it to boot. |
createNewTemporarySite(siteSlug?, settings?) | Creates and activates a new temporary site. |
The API is available after Playground loads its saved sites. If window.playgroundSites is undefined, wait until the Playground UI has finished loading. Then call await window.playgroundSites.isReady() before running commands that need the active site’s client.
See the Sites API documentation for the complete reference, including methods for autosaved and explicitly saved sites.
Run the examples on the Playground website
The examples below use the Sites API on playground.wordpress.net. Open the website, wait for the Playground UI to load, open your browser’s DevTools console, and run await window.playgroundSites.isReady(). You can then paste the examples into the console. A browser automation tool such as Playwright can run the same JavaScript while it controls a tab open to the Playground website.
This API belongs to the Playground website. It is not part of the @wp-playground/client package or the Playground CLICLI Command Line Interface. Terminal (Bash) in Mac, Command Prompt in Windows, or WP-CLI for WordPress., and it is not exposed by /remote.html when you embed Playground in another application. For embedded sites and test automation that starts its own Playground client, use the JavaScript API instead.
Scenario 1: Create a clean plugin test site
When debugging a plugin issue, start with a fresh site that matches the target environment. The example below creates a temporary site using PHP 8.4, the latest WordPress release, and networking enabled:
const slug = await window.playgroundSites.createNewTemporarySite(
"plugin-test-php-84",
{
phpVersion: "8.4",
wpVersion: "latest",
networking: true,
}
);
console.log(`Active test site: ${slug}`);
Use this when you need a clean environment before installing a plugin or reproducing a report. The site is temporary at this point, so changes will not survive a reload.
Scenario 2: Save and rename a reproducible bug report
Once you reproduce a bug, save the site before making more changes. Saving turns a temporary site into a browser-stored site:
const saved = await window.playgroundSites.saveInBrowser(
"Plugin compatibility test"
);
console.log(saved);
The returned object includes the site slug and storage type:
{
slug: "plugin-test-php-84",
storage: "opfs",
}
After saving, you can rename the active site:
await window.playgroundSites.rename(
"Plugin compatibility test - PHP 8.4"
);
That sequence matters. rename() only works on saved sites. If you call it on a temporary site, Playground throws:
Cannot rename a temporary site. Save it first.
Scenario 3: Switch between saved test sites
When you keep separate sites for different reproduction cases, list them and switch by slug:
const sites = window.playgroundSites.list();
console.table(
sites.map((site) => ({
slug: site.slug,
name: site.name,
storage: site.storage,
active: site.isActive,
}))
);
To activate one:
const target = window.playgroundSites
.list()
.find((site) => site.name.includes("Plugin compatibility"));
if (target) {
await window.playgroundSites.setActiveSite(target.slug);
}
setActiveSite() waits for the selected site to boot before resolving, so follow-up commands can safely assume the active site is ready.
Scenario 4: Inspect plugin state with PHP
The site management API gives you access to the active site’s PlaygroundClient. That client can run PHP inside the WordPress environment.
function phpResponseText(response) {
return "text" in response
? response.text
: new TextDecoder().decode(response.bytes);
}
await window.playgroundSites.isReady();
const client = window.playgroundSites.getClient();
if (!client) {
throw new Error("The active Playground site has not booted yet.");
}
const response = await client.run({
code: `<?php
require_once "/wordpress/wp-load.php";
echo json_encode([
"php" => phpversion(),
"wp" => get_bloginfo("version"),
"active_plugins" => get_option("active_plugins"),
]);
`,
});
console.log(phpResponseText(response));
This lets you query plugin data in the WordPress database directly, without navigating through wp-admin. You can replace the PHP with targeted checks for options, active theme data, custom post types, or plugin-specific database rows.
For example, to inspect one option:
const response = await client.run({
code: `<?php
require_once "/wordpress/wp-load.php";
echo wp_json_encode(get_option("woocommerce_currency"));
`,
});
console.log(phpResponseText(response));
Playground uses SQLite for WordPress storage, so prefer WordPress APIs like get_option() and $wpdb over database-specific SQL behavior.
Scenario 5: Change runtime settings for a saved site
Sometimes a bug only appears with a specific PHP version or with networking enabled. Once the active site is saved, you can update runtime settings directly:
await window.playgroundSites.setPhpVersion("8.3");
await window.playgroundSites.setNetworking(true);
These methods require a saved site. If the active site is temporary, save it first:
await window.playgroundSites.saveInBrowser("Network test");
await window.playgroundSites.setNetworking(true);
This is a good workflow for compatibility testing:
- Create a clean site.
- Install and configure the plugin.
- Save the site.
- Switch PHP versions.
- Re-run the same plugin checks.
Scenario 6: Create a small compatibility checklist
You can combine the API calls into a manual checklist from DevTools. This example creates a site, saves it, gathers basic version information, and leaves the result in the console:
async function prepareCompatibilitySite() {
function phpResponseText(response) {
return "text" in response
? response.text
: new TextDecoder().decode(response.bytes);
}
const slug = await window.playgroundSites.createNewTemporarySite(
"compatibility-check",
{
phpVersion: "8.4",
wpVersion: "latest",
networking: true,
}
);
await window.playgroundSites.saveInBrowser(
"Compatibility check - PHP 8.4"
);
await window.playgroundSites.isReady();
const client = window.playgroundSites.getClient();
if (!client) {
throw new Error("The compatibility site has not booted yet.");
}
const response = await client.run({
code: `<?php
require_once "/wordpress/wp-load.php";
echo wp_json_encode([
"site_url" => get_site_url(),
"php" => phpversion(),
"wp" => get_bloginfo("version"),
"theme" => wp_get_theme()->get("Name"),
]);
`,
});
return {
site: window.playgroundSites
.list()
.find((site) => site.slug === slug),
info: JSON.parse(phpResponseText(response)),
};
}
console.log(await prepareCompatibilitySite());
This is not a replacement for a full test suite. It is a fast way to prepare and inspect a browser-based testing site when you are triaging a report.
How this fits recent Playground updates
Recent Playground work also added deeper AI and browser automation hooksHooks In WordPress theme and development, hooks are functions that can be applied to an action or a Filter in WordPress. Actions are functions performed when a certain event occurs in WordPress. Filters allow you to modify certain functions. Arguments used to hook both filters and actions look the same. through MCP and WebMCP. The same centralized site management API supports those integrations, so AI agents and browser-native tools can manage Playground sites through explicit operations instead of clicking through menus.
For example:
list()maps naturally to “show me my Playground sites.”setActiveSite(slug)maps to “open the site named Plugin compatibility test.”saveInBrowser(name)maps to “save this reproduction so I can come back later.”getClient()gives tool layers access to PHP execution, 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 filesystem operations.
The result is a cleaner foundation for agent workflows. A coding agent can keep one site for investigation, another for a clean reproduction, and another for verifying a fix, all without depending on visual UI state.
For setup instructions, see Connect AI coding agents to WordPress Playground with MCP. For URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org-based configuration options such as php, wp, networking, and site-slug, see the Query API documentation.
Practical limits
There are a few details to remember:
window.playgroundSitesis only available after Playground finishes loading saved sites.- Temporary sites must be saved before you can rename them, delete them, or update their PHP/networking settings.
- Call
isReady()beforegetClient()when the active site may still be booting. Until the site is ready,getClient()can returnundefined. saveToLocalFileSystem()may open a browser directory picker when you do not pass a directory handle.- Browser storage is still browser storage. Clearing site data can remove saved Playground sites.
Try it
Open playground.wordpress.net, wait for the site to finish loading, then open DevTools and run:
await window.playgroundSites.isReady();
window.playgroundSites.list();
From there, create a temporary site, save it, switch between saved sites, and use getClient() to inspect WordPress state with PHP. For plugin and theme developers, this turns Playground from a single sandbox into a small fleet of browser-based test environments.