In brief: open playground.wordpress.net, wait for window.playgroundSites.isReady(), define the version combinations, then call createNewSavedSite() for each combination. Next, use list(), setActiveSite() and a PHP check to verify the result.
In the current documentation, the official name is Sites API; “Site Manager API” is a descriptive name for its site-management functionality. This API is suitable for creating, saving, renaming, switching, and deleting Playground sites. It does not directly accept a plugin, theme, or blueprint-url; those steps belong to Blueprints or the JavaScript API (according to wordpress.github.io).
This article uses illustrative version strings. Replace <wp-version> and <php-version> with versions supported by Playground and by your test. Do not use a version simply because it appears in the example; supported versions may change over time.
Choose the API for the task you need to perform
| Goal | Recommended tool | Result |
|---|---|---|
| Compare WordPress, PHP, or networking | Sites API | Multiple sites created and saved in the browser |
| Install a plugin, theme, or sample content | Blueprints | An initialization configuration with predefined steps |
| Initialize multiple independent test environments | JavaScript API + Blueprints | Each client or iframe is a separate environment |
| Rename, switch, or delete a saved site | Sites API | Site lifecycle management |
If the matrix contains only WordPress × PHP × networking, start with the Sites API. If each cell also needs a plugin, theme, or sample data, keep the matrix but initialize each cell with a Blueprint and the JavaScript API.
Prepare and define the matrix
You need a JavaScript-enabled browser, access to Playground, and a list of the versions you want to test. Run the code below in the DevTools for the Playground page, not in the WordPress administration dashboard or on a production server.
The following example creates four combinations. Values marked with <...> are required placeholders that you must replace before running the code:
const matrix = [
{ wp: '<wp-version>', php: '<php-version-1>', networking: false },
{ wp: '<wp-version>', php: '<php-version-1>', networking: true },
{ wp: '<wp-version>', php: '<php-version-2>', networking: false },
{ wp: '<wp-version>', php: '<php-version-2>', networking: true },
];
This example demonstrates how to create a matrix; it does not prove that every combination is compatible. Before running it, verify that the version strings are specific versions supported by Playground. Use specific versions instead of latest when you need reproducible results.
Wait for the Sites API to be ready
window.playgroundSites may not appear immediately after the page loads. The function below waits for up to 30 seconds and then calls isReady(). This timeout prevents the wait loop from running indefinitely if the page fails or storage access is blocked.
async function waitForSitesAPI(timeoutMs = 30000) {
const startedAt = Date.now();
while (!window.playgroundSites) {
if (Date.now() - startedAt > timeoutMs) {
throw new Error('Không tìm thấy window.playgroundSites trong thời gian chờ.');
}
await new Promise((resolve) => setTimeout(resolve, 100));
}
await window.playgroundSites.isReady();
return window.playgroundSites;
}
const sites = await waitForSitesAPI();
console.table(sites.list());
Run the code in the DevTools Console on the Playground tab itself. You should see a table of the sites that currently exist. If the Console does not allow await at the top level, wrap the commands in a async function or run each block after the API is ready.
If you receive the error playgroundSites is undefined, check the URL, wait for the page to finish loading, and try again. If isReady() does not complete, reopen the Playground tab before creating sites; do not continue running site-creation commands while the application is not ready.
Create sites sequentially from the matrix
The following code uses createNewSavedSite(slug, settings, options). Each site is saved with persistence: 'explicit', has its own slug, and does not change the browser URL because of updateUrl: false. Run it after defining matrix and waitForSitesAPI().
async function createMatrixSites(matrix) {
const sites = await waitForSitesAPI();
const created = [];
for (const item of matrix) {
const slug = [
'matrix',
`wp-${item.wp.replace(/\./g, '-')}`,
`php-${item.php.replace(/\./g, '-')}`,
item.networking ? 'net-on' : 'net-off',
].join('-');
const name = `WordPress ${item.wp} / PHP ${item.php} / ${item.networking ? 'networking bật' : 'networking tắt'}`;
try {
const createdSlug = await sites.createNewSavedSite(
slug,
{
wpVersion: item.wp,
phpVersion: item.php,
networking: item.networking,
},
{
persistence: 'explicit',
updateUrl: false,
}
);
await sites.rename(name, createdSlug);
created.push({
slug: createdSlug,
name,
wp: item.wp,
php: item.php,
networking: item.networking,
status: 'created',
});
console.log(`Đã tạo: ${name}`);
} catch (error) {
created.push({
slug,
name,
wp: item.wp,
php: item.php,
networking: item.networking,
status: 'failed',
error: String(error),
});
console.error(`Không tạo được ${name}`, error);
}
}
return created;
}
const result = await createMatrixSites(matrix);
console.table(result);
console.table(window.playgroundSites.list());
The code runs sequentially, so each site finishes creating before the next combination begins. This makes it easier to track the active site and errors for each matrix cell than creating sites concurrently. The result created only indicates that the creation operation completed; it does not demonstrate compatibility with a plugin, theme, or application functionality.
How to verify the site list
In the result from list(), compare the slug, name, persistence type, and active status using the fields returned by your API version. Do not treat the display name as evidence of the runtime version; read the version again from the active site during the PHP check.
To switch to a specific site, replace the slug below with the actual slug from the result list():
const slugToCheck = 'matrix-wp-<wp-version>-php-<php-version-1>-net-on';
await window.playgroundSites.setActiveSite(slugToCheck);
await window.playgroundSites.isReady();
console.log(window.playgroundSites.getClient());
If the slug does not exist, setActiveSite() will fail or will not switch sites. Copy the slug from the actual result instead of guessing how dots or variant names are formatted.
Check the WordPress version and status
After selecting a site, use the active site's client to run a minimal PHP snippet. This code only checks that WordPress can load and reports which version is running; it does not test plugin or theme functionality.
const client = window.playgroundSites.getClient();
if (!client) {
throw new Error('Site hiện tại chưa sẵn sàng.');
}
const response = await client.run({
code: `<?php
require '/wordpress/wp-load.php';
echo wp_json_encode([
'wp_version' => get_bloginfo('version'),
'php_version' => PHP_VERSION,
'site_url' => site_url(),
]);
`,
});
console.log(response);
Compare wp_version and php_version with the combination you are testing. If the values do not match, stop the comparison; check the slug, the versions used during initialization, and the site's status before trying again. If the site starts but this check fails, that indicates an initialization or WordPress loading error, not compatibility evidence.
For a plugin or theme, add separate checks: confirm that the plugin is installed and activated, that its main page or command runs, that the logs contain no errors, and that the result meets the criteria of the test. A successfully started site is not a substitute for a functional test suite.
Use Blueprints when you need a plugin or theme

createNewSavedSite() is not where you pass plugin, theme or blueprint-url. Blueprints describe initialization steps such as installPlugin, installTheme, login and runPHP (according to wordpress.github.io).
The Blueprint below is a structurally valid example. Replace your-plugin-slug and your-theme-slug with the actual slugs from WordPress.org before using it:
{
"$schema": "https://playground.wordpress.net/blueprint-schema.json",
"preferredVersions": {
"wp": "<wp-version>",
"php": "<php-version>"
},
"login": true,
"steps": [
{
"step": "installPlugin",
"pluginData": {
"resource": "wordpress.org/plugins",
"slug": "your-plugin-slug"
}
},
{
"step": "installTheme",
"themeData": {
"resource": "wordpress.org/themes",
"slug": "your-theme-slug"
},
"options": {
"activate": true
}
}
]
}
This is JSON data, not a command to run directly in the Console. Save it as a Blueprint file or pass it to the JavaScript API using the method supported by your application. For a custom plugin or theme, use a direct download URL for the ZIP file or a supported resource; do not use an HTML repository page in place of an installation file (according to wordpress.github.io).
Initialize multiple plugin environments with the JavaScript API
When each matrix cell needs its own plugin, create a Playground client for each cell. The example below creates an iframe, initializes the client, and waits for each client to become ready. Replace the placeholders before running it in an HTML page with module support:
<div id="playgrounds"></div>
<script type="module">
import { startPlaygroundWeb } from 'https://playground.wordpress.net/client/index.js';
const testCases = [
{ wp: '<wp-version>', php: '<php-version-1>', plugin: 'your-plugin-slug' },
{ wp: '<wp-version>', php: '<php-version-2>', plugin: 'your-plugin-slug' },
];
async function startCase(testCase) {
const iframe = document.createElement('iframe');
iframe.title = `WordPress ${testCase.wp} / PHP ${testCase.php}`;
iframe.width = '100%';
iframe.height = '600';
document.querySelector('#playgrounds').appendChild(iframe);
const client = await startPlaygroundWeb({
iframe,
remoteUrl: 'https://playground.wordpress.net/remote.html',
blueprint: {
preferredVersions: {
wp: testCase.wp,
php: testCase.php,
},
login: true,
steps: [
{
step: 'installPlugin',
pluginData: {
resource: 'wordpress.org/plugins',
slug: testCase.plugin,
},
},
],
},
});
await client.isReady();
return client;
}
const clients = [];
for (const testCase of testCases) {
clients.push(await startCase(testCase));
}
console.log(`Đã khởi tạo ${clients.length} môi trường.`);
</script>
This code uses the JavaScript API directly for the iframes; the iframes do not automatically become sites managed by window.playgroundSites . The Sites API is provided at the Playground application layer, while the embedded clients are managed in your code (according to wordpress.github.io).
Clean up and roll back after testing
An explicit-persistence site is retained until you delete it. Before deleting it, save the test results or export any information you need; deletion is a cleanup step, not a way to recover data.
const sitesToDelete = window.playgroundSites
.list()
.filter((site) => site.slug.startsWith('matrix-'));
for (const site of sitesToDelete) {
await window.playgroundSites.delete(site.slug);
console.log(`Đã xóa ${site.slug}`);
}
console.table(window.playgroundSites.list());
Delete only the slugs that you have confirmed belong to the test matrix. Do not use a shared prefix if the browser still contains sites that must be kept. For tests that do not need to be retained, consider createNewTemporarySite(); a temporary site is not suitable for preserving results between sessions.
If a cell fails, retain its log and slug, correct the configuration, and recreate only the failed cell. Do not delete the entire matrix before identifying the cause. For reproducible results, pin the WordPress and PHP versions and use fixed releases or commits for plugin and theme resources.
Quick diagnosis of common errors
| Symptom | Possible cause | What to do |
|---|---|---|
window.playgroundSites does not exist yet | The Playground application has not finished loading | Run waitForSitesAPI(), confirm that you are on the correct page, and try again after the page has finished loading. |
| Cannot create the version | The version string is unsupported by the runtime or has an invalid format | Replace the placeholder with a specific supported version and read the full error returned. |
The site does not appear in list() | The operation failed, browser storage is blocked, or the site was created temporarily | Check result, the browser’s storage permissions, and the persistence: 'explicit'option. |
| The plugin or theme was skipped | The parameters were passed to the Sites API incorrectly | Use a Blueprint with the JavaScript API, or run the installation process separately with a client. |
| The plugin or theme ZIP could not be loaded | The URL returns HTML, requires authentication, has expired, or does not meet access requirements | Check the direct download URL, access permissions, ZIP format, and resources supported by Playground. |
| Results vary between runs | Use latest, changing resources, or a plugin that updates itself | Pin versions and use a specific release or commit when reproducibility is required. |
| There is not enough memory, or sites are difficult to locate | Too many explicit sites were created | Record the results, delete test sites you do not need to keep, and use a temporary site for short-lived tests. |
If your goal is to test a plugin before updating a live website, separate the two tasks: create a clean environment and run a functional test suite. See also the WordPress plugin and theme testing checklist and the plugin pre-update verification process. Playground enables testing in a browser; it should not be treated as a complete replica of production infrastructure.
Completion checklist
- The code was run in Playground’s DevTools, not in the WordPress dashboard.
- Called
isReady()before using the Sites API. - All placeholders were replaced with actual versions and resource slugs.
- Each combination has its own slug and was created sequentially.
- Used
list()to cross-check sites, then usedsetActiveSite()andgetClient()for verification. - The WordPress and PHP versions were read back from the active site rather than inferred solely from the site name.
- A Blueprint or the JavaScript API was used for plugins, themes, and sample data.
- The necessary results were saved, and unused explicit sites were deleted.
The key distinction to remember is: The Sites API manages which sites exist and how they are persisted; Blueprints describe which components a site is initialized with. Separating these two responsibilities makes version matrices easier to manage and prevents incorrect assumptions about the API’s parameters.

