---
title: "Preview plugins in WordPress Playground directly from the Pull Request"
description: "When someone opens a PR in a WordPress plugin, the usual process for reviewing it involves downloading the branch, installing it locally, activating the plugin, and testing the changes by hand. That has frictions: you have to have a prepared local environment and dedicate time to install something that is perhaps half broken. If the reviewer is not a developer, they can't do it directly. With this setup, the PR itself generates a link that opens the plugin running in the browser. One click, and you're already trying. What is WordPress Playground? WordPress Playground is a WordPress environment that runs entirely on the... Read more"
url: https://davidperezgar.com/en/blog/preview-of-plugins-in-wordpress-playground-directly-from-the-pull-request/
date: 2026-05-23
modified: 2026-05-24
author: "David Pérez"
image: https://davidperezgar.com/en/wp-content/uploads/sites/4/preview-playground-wordpress-1-scaled.avif
categories: ["Blog"]
type: post
lang: en
---

# Preview plugins in WordPress Playground directly from the Pull Request

When someone opens a PR in a WordPress plugin, the usual process for reviewing it involves downloading the branch, installing it locally, activating the plugin, and testing the changes by hand. That has frictions: you have to have a prepared local environment and dedicate time to install something that is perhaps half broken. If the reviewer is not a developer, they can't do it directly.

With this setup, the PR itself generates a link that opens the plugin running in the browser. One click, and you're already trying.

## What is WordPress Playground?

WordPress Playground is a WordPress environment that runs entirely in-browser, serverless. Use WebAssembly (WASM) to run PHP directly on the client. You don't need Docker, XAMPP, or anything installed locally.

Anyone can open a URL and have a functional WordPress in seconds, with the plugin installed and activated. When you close the tab, everything disappears. No trace, no facilities, no conflicts.

This makes it perfect for code reviews, quick demos, and plugin testing without compromising any real environment.

## How the complete system works

There are three pieces that fit together:

- **build-dist.yml** — runs on every push. It installs the Composer dependencies without `--dev`, generates a `blueprint.json` pointing to that particular branch, and forces a push to a branch `dist/nombre-rama`. It also deletes the dist branch when the original is deleted.

- **playground-preview.yml** — Runs when a PR is opened, reopened, or updated. Build the Playground URL with the dist branch blueprint and post or update a comment in the PR. Use HTML marker so you don't duplicate comments.

- **pr-blueprint.json** — blueprint template to check the trunk manually, without opening a PR. Define the landing page within wp-admin, automatically log in as admin, install the plugin from the zip URL and activate it.

## Step-by-step guide

### 1. Create the folder structure

You need two folders at the root of the repository: `.github/workflows/` for workflows and `.github/` for the blueprint.

### 2. Add build-dist.yml

This workflow does three things: it installs dependencies, generates the blueprint with the correct branch, and pushes the dist branch. Adjust the `landingPage` plugin and plugin name in the blueprint generation section.

```
name: Build dist branch

on:
push:
branches:
- '**'
delete:

permissions:
contents: write

jobs:
build:
if: github.event_name == 'push' && !startsWith(github.ref_name, 'dist/')
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
tools: composer

- name: Install production dependencies
run: composer install --no-dev --optimize-autoloader

- name: Generate Playground blueprint
env:
BRANCH_NAME: ${{ github.ref_name }}
run: |
mkdir -p .wordpress-playground
cat > .github/pr-blueprint.json

### 3. Add playground-preview.yml

This workflow publishes the comment to the PR. HTML markup prevents duplicate comments from being created on each push. Change it to the name of your plugin.

```
name: Playground PR Preview

on:
pull_request:
types:

permissions:
issues: write
pull-requests: write

jobs:
playground-preview:
name: Comment with Playground preview link
runs-on: ubuntu-latest
steps:
- name: Post Playground preview comment
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const marker = '';
const issue_number = context.payload.pull_request.number;
const branchName = context.payload.pull_request.head.ref;
const owner = context.repo.owner;
const repo = context.repo.repo;

const blueprintUrl = `https://raw.githubusercontent.com/${owner}/${repo}/dist/${branchName}/.wordpress-playground/blueprint.json`;
const playgroundUrl = `https://playground.wordpress.net/?blueprint-url=${encodeURIComponent(blueprintUrl)}`;

const body = [
marker,
'## Test in WordPress Playground',
`[**Open PR #${issue_number} in Playground ↗**](${playgroundUrl})`,
' The link becomes active once the **Build dist branch** workflow finishes.',
'- Login: `admin` / `password`',
'- The plugin is installed and activated automatically.',
].join('\n');

const comments = await github.paginate(
github.rest.issues.listComments,
{ owner, repo, issue_number, per_page: 100 }
);
const existing = comments.find(c => c.body?.includes(marker));

if (existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
} else {
await github.rest.issues.createComment({ owner, repo, issue_number, body });
}
```

### 4. Check workflow permissions

In `build-dist.yml` Coming `permissions: contents: write`. In `playground-preview.yml` you need `issues: write` and `pull-requests: write`. If your repo has the Actions with restricted permissions by default, review it in *Settings → Actions → General → Workflow permissions*.

### 5. Make the first push and check

Push any branch, go to *Actions* , and check that the *Build dist branch* workflow ends in green. On the repo branches it should appear `dist/nombre-de-tu-rama` with the vendor and blueprint inside.

### 6. Open a PR and wait for the automatic comment

Opening the PR executes the *Playground PR Preview* workflow. In a few seconds you will see a comment with the direct link to Playground with the plugin installed, login made as admin and the settings page open.

## Customize the blueprint

The blueprint is a JSON that Playground interprets. You can add more steps depending on what your plugin needs.

!(https://davidperezgar.com/wp-content/uploads/github-comment-playground.png)

### Install a dependency plugin

```
{
"step": "installPlugin",
"pluginZipFile": {
"resource": "wordpress.org/plugins",
"slug": "woocommerce"
},
"options": { "activate": true }
}
```

### Create Test Data with WP-CLI

```
{
"step": "wp-cli",
"command": "wp post create --post_title='Test product' --post_status=publish"
}
```

### Fix WordPress or PHP version

```
{
"$schema": "https://playground.wordpress.net/blueprint-schema.json",
"preferredVersions": {
"php": "8.1",
"wp": "6.4"
}
}
```

## Important Notes

The Composer vendor registers in the dist branch with `git add -f vendor/`. It's intentional: Playground downloads the zip from that branch and needs all the dependencies included. It doesn't conflict with you `.gitignore` because it only happens in the dist branch.

The build is forced (`--force`), so the dist branch always reflects the current state. There is no accumulated history.

If a PR receives several pushes in a row, the workflow updates the same comment instead of creating a new one. That is controlled by the HTML marker. Change it to something unique to your plugin to avoid collisions if you have multiple repos with the same setup.

Full documentation: (https://wordpress.github.io/wordpress-playground/guides/github-action-pr-preview/)
