--- title: Build your first Blueprint slug: /blueprints/tutorial/build-your-first-blueprint description: A step-by-step tutorial to build your first Blueprint. Learn to install themes, plugins, and import site content. --- Let's build an elementary Blueprint that 1. Creates a new WordPress site 2. Sets the site title to "My first Blueprint" 3. Installs the _Adventurer_ theme 4. Installs the _Hello Dolly_ plugin from the WordPress plugin directory 5. Installs a custom plugin 6. Changes the site content ## 1. Create a new WordPress site Let's start by creating a `blueprint.json` file with the following contents: ```json {} ``` It may seem like nothing is happening, but this Blueprint already spins up a WordPress site with the latest major version. [   Run Blueprint   ](https://playground.wordpress.net/#{})
**Autocomplete** If you use an IDE, like VS Code or PHPStorm, you can use the [Blueprint JSON Schema](https://playground.wordpress.net/blueprint-schema.json) for an autocompleted Blueprint development experience. Add the following line at the top of your `blueprint.json` file: ```json { "$schema": "https://playground.wordpress.net/blueprint-schema.json" } ```
Here's what it looks like in VS Code: ![Autocompletion visualized](https://raw.githubusercontent.com/WordPress/wordpress-playground/refs/heads/trunk/packages/docs/site/static/img/blueprints/schema-autocompletion.webp) ## 2. Set the site title to "My first Blueprint" Blueprints consist of a series of [steps](/blueprints/steps) that define how to build a WordPress site. Before you write the first step, declare an empty list of steps: ```json { "$schema": "https://playground.wordpress.net/blueprint-schema.json", "steps": [] } ``` This Blueprint isn't very exciting—it creates the same default site as the empty Blueprint above. Let's do something about it! WordPress stores the site title in the `blogname` option. Add your first step and set that option to "My first Blueprint": ```json { "$schema": "https://playground.wordpress.net/blueprint-schema.json", "steps": [ { "step": "setSiteOptions", "options": { "blogname": "My first Blueprint" } } ] } ``` [   Run Blueprint   ](https://playground.wordpress.net/#https://playground.wordpress.net/#eyIkc2NoZW1hIjoiaHR0cHM6Ly9wbGF5Z3JvdW5kLndvcmRwcmVzcy5uZXQvYmx1ZXByaW50LXNjaGVtYS5qc29uIiwic3RlcHMiOlt7InN0ZXAiOiJzZXRTaXRlT3B0aW9ucyIsIm9wdGlvbnMiOnsiYmxvZ25hbWUiOiJNeSBmaXJzdCBCbHVlcHJpbnQifX1dfQ==) The [`setSiteOptions` step](/blueprints/steps#SetSiteOptionsStep) specifies the site options in the WordPress database. The `options` object contains the key-value pairs to set. In this case, you changed the value of the `blogname` key to "My first Blueprint". You can read more about all available steps in the [Blueprint Steps API Reference](/blueprints/steps). ### Shorthands You can specify some steps using a shorthand syntax. For example, you could write the `setSiteOptions` step like this: ```json { "$schema": "https://playground.wordpress.net/blueprint-schema.json", "siteOptions": { "blogname": "My first Blueprint" } } ``` The shorthand syntax and the step syntax correspond with each other. Every step specified with the shorthand syntax is automatically added at the beginning of the `steps` array in an arbitrary order. Which should you choose? Use shorthands when brevity is your main concern, use steps when you need more control over the order of execution. ## 3. Install the _Adventurer_ theme Adventurer is an open-source theme [available in the WordPress theme directory](https://wordpress.org/themes/adventurer/). Let's install it using the [`installTheme` step](/blueprints/steps#InstallThemeStep): ```json { "siteOptions": { "blogname": "My first Blueprint" }, "steps": [ { "step": "installTheme", "themeData": { "resource": "wordpress.org/themes", "slug": "adventurer" } } ] } ``` [   Run Blueprint   ](https://playground.wordpress.net/#eyIkc2NoZW1hIjoiaHR0cHM6Ly9wbGF5Z3JvdW5kLndvcmRwcmVzcy5uZXQvYmx1ZXByaW50LXNjaGVtYS5qc29uIiwib3B0aW9ucyI6eyJibG9nbmFtZSI6Ik15IGZpcnN0IEJsdWVwcmludCJ9LCJzdGVwcyI6W3sic3RlcCI6Imluc3RhbGxUaGVtZSIsInRoZW1lWmlwRmlsZSI6eyJyZXNvdXJjZSI6IndvcmRwcmVzcy5vcmcvdGhlbWVzIiwic2x1ZyI6ImFkdmVudHVyZXIifX1dfQ==) The site should now look like the screenshot below: ![Site with the adventurer theme](https://raw.githubusercontent.com/WordPress/wordpress-playground/refs/heads/trunk/packages/docs/site/static/img/blueprints/installed-adventurer-theme.webp) ### Resources The `themeData` defines a [resource](/blueprints/steps/resources) and references an external file required to complete the step. Playground supports different types of resources, including - `url`, - `wordpress.org/themes`, - `wordpress.org/plugins`, - `vfs`(virtual file system), or - `literal`. The example uses the `wordpress.org/themes` resource, which requires a `slug` identical to the one used in WordPress theme directory: In this case, `https://wordpress.org/themes//` becomes `https://wordpress.org/themes/adventurer/`.
Learn more about the supported resources in the [Blueprint Resources API Reference](/blueprints/steps/resources/).
## 4. Install the _Hello Dolly_ plugin A classic WordPress plugin that displays random lyrics from the song "Hello, Dolly!" in the admin dashboard. Let's install it using the [`installPlugin` step](/blueprints/steps#InstallPluginStep): ```json { "siteOptions": { "blogname": "My first Blueprint" }, "steps": [ { "step": "installTheme", "themeData": { "resource": "wordpress.org/themes", "slug": "adventurer" } }, { "step": "installPlugin", "pluginData": { "resource": "wordpress.org/plugins", "slug": "hello-dolly" } } ] } ``` [   Run Blueprint   ](https://playground.wordpress.net/#eyJzaXRlT3B0aW9ucyI6eyJibG9nbmFtZSI6Ik15IGZpcnN0IEJsdWVwcmludCJ9LCJzdGVwcyI6W3sic3RlcCI6Imluc3RhbGxUaGVtZSIsInRoZW1lWmlwRmlsZSI6eyJyZXNvdXJjZSI6IndvcmRwcmVzcy5vcmcvdGhlbWVzIiwic2x1ZyI6ImFkdmVudHVyZXIifX0seyJzdGVwIjoiaW5zdGFsbFBsdWdpbiIsInBsdWdpblppcEZpbGUiOnsicmVzb3VyY2UiOiJ3b3JkcHJlc3Mub3JnL3BsdWdpbnMiLCJzbHVnIjoiaGVsbG8tZG9sbHkifX1dfQ==) The Hello Dolly plugin is now installed and activated. Like the `themeData`, the `pluginData` defines a reference to an external file required for the step. The example uses the `wordpress.org/plugins` resource to install the plugin with the matching `slug` from the WordPress plugin directory. ## 5. Install a custom plugin Let's install a custom WordPress plugin that adds a message to the admin dashboard: ```php Hello from My Custom Plugin!'; } add_action('admin_notices', 'my_custom_plugin'); ``` You can use the [installPlugin](/blueprints/steps#InstallPluginStep), but that requires creating a ZIP file. Let's start with something different to see if the plugin works: 1. Create a `wp-content/plugins/hello-from-the-dashboard` directory using the [`mkdir` step](/blueprints/steps#MkdirStep). 2. Write a `plugin.php` file using the [`writeFile` step](/blueprints/steps#WriteFileStep). 3. Activate the plugin using the [`activatePlugin` step](/blueprints/steps#ActivatePluginStep). Here's what that looks like in a Blueprint: ```json { // ... "steps": [ // ... { "step": "mkdir", "path": "/wordpress/wp-content/plugins/hello-from-the-dashboard" }, { "step": "writeFile", "path": "/wordpress/wp-content/plugins/hello-from-the-dashboard/plugin.php", "data": "Hello from My Custom Plugin!';\n}\n\nadd_action('admin_notices', 'my_custom_plugin');" }, { "step": "activatePlugin", "pluginPath": "hello-from-the-dashboard/plugin.php" } ] } ``` The last thing to do is log the user in as an admin. You can do that with a shorthand of the [`login` step](/blueprints/steps#LoginStep): ```json { "login": true, "steps": { // ... } } ``` Here's the complete Blueprint: ```json { "$schema": "https://playground.wordpress.net/blueprint-schema.json", "login": true, "siteOptions": { "blogname": "My first Blueprint" }, "steps": [ { "step": "installTheme", "themeData": { "resource": "wordpress.org/themes", "slug": "adventurer" } }, { "step": "installPlugin", "pluginData": { "resource": "wordpress.org/plugins", "slug": "hello-dolly" } }, { "step": "mkdir", "path": "/wordpress/wp-content/plugins/hello-from-the-dashboard" }, { "step": "writeFile", "path": "/wordpress/wp-content/plugins/hello-from-the-dashboard/plugin.php", "data": "Hello from My Custom Plugin!';\n}\n\nadd_action('admin_notices', 'my_custom_plugin');" }, { "step": "activatePlugin", "pluginPath": "hello-from-the-dashboard/plugin.php" } ] } ``` [   Run Blueprint   ](https://playground.wordpress.net/#eyJsb2dpbiI6dHJ1ZSwic2l0ZU9wdGlvbnMiOnsiYmxvZ25hbWUiOiJNeSBmaXJzdCBCbHVlcHJpbnQifSwic3RlcHMiOlt7InN0ZXAiOiJpbnN0YWxsVGhlbWUiLCJ0aGVtZVppcEZpbGUiOnsicmVzb3VyY2UiOiJ3b3JkcHJlc3Mub3JnL3RoZW1lcyIsInNsdWciOiJhZHZlbnR1cmVyIn19LHsic3RlcCI6Imluc3RhbGxQbHVnaW4iLCJwbHVnaW5aaXBGaWxlIjp7InJlc291cmNlIjoid29yZHByZXNzLm9yZy9wbHVnaW5zIiwic2x1ZyI6ImhlbGxvLWRvbGx5In19LHsic3RlcCI6Im1rZGlyIiwicGF0aCI6Ii93b3JkcHJlc3Mvd3AtY29udGVudC9wbHVnaW5zL2hlbGxvLW9uLXRoZS1kYXNoYm9hcmQifSx7InN0ZXAiOiJ3cml0ZUZpbGUiLCJwYXRoIjoiL3dvcmRwcmVzcy93cC1jb250ZW50L3BsdWdpbnMvaGVsbG8tb24tdGhlLWRhc2hib2FyZC9wbHVnaW4ucGhwIiwiZGF0YSI6Ijw/cGhwXG4vKlxuUGx1Z2luIE5hbWU6IFwiSGVsbG9cIiBvbiB0aGUgRGFzaGJvYXJkXG5EZXNjcmlwdGlvbjogQSBjdXN0b20gcGx1Z2luIHRvIHNob3djYXNlIFdvcmRQcmVzcyBCbHVlcHJpbnRzXG5WZXJzaW9uOiAxLjBcbkF1dGhvcjogV29yZFByZXNzIENvbnRyaWJ1dG9yc1xuKi9cblxuZnVuY3Rpb24gbXlfY3VzdG9tX3BsdWdpbigpIHtcbiAgICBlY2hvICc8aDE+SGVsbG8gZnJvbSBNeSBDdXN0b20gUGx1Z2luITwvaDE+Jztcbn1cblxuYWRkX2FjdGlvbignYWRtaW5fbm90aWNlcycsICdteV9jdXN0b21fcGx1Z2luJyk7In0seyJzdGVwIjoiYWN0aXZhdGVQbHVnaW4iLCJwbHVnaW5QYXRoIjoiaGVsbG8tb24tdGhlLWRhc2hib2FyZC9wbHVnaW4ucGhwIn1dfQ==) That's what it looks like when you navigate to the dashboard: ![Site with the custom plugin](https://raw.githubusercontent.com/WordPress/wordpress-playground/refs/heads/trunk/packages/docs/site/static/img/blueprints/installed-custom-plugin.webp) ### Create a plugin and zip it Encoding PHP files as `JSON` can be useful for quick testing, but it's inconvenient and difficult to read. Instead, create a file with the plugin code, compress it, and use the `ZIP` file as the `resource` in the [`installPlugin` step](/blueprints/steps#InstallPluginStep) to install it (the path in the `URL` should match the one in your GitHub repository): ```json { "$schema": "https://playground.wordpress.net/blueprint-schema.json", "login": true, "siteOptions": { "blogname": "My first Blueprint" }, "steps": [ { "step": "installTheme", "themeData": { "resource": "wordpress.org/themes", "slug": "adventurer" } }, { "step": "installPlugin", "pluginData": { "resource": "wordpress.org/plugins", "slug": "hello-dolly" } }, { "step": "installPlugin", "pluginData": { "resource": "url", "url": "https://raw.githubusercontent.com/wordpress/blueprints/trunk/docs/assets/hello-from-the-dashboard.zip" } } ] } ``` You can shorten that Blueprint even more using the shorthand syntax: ```json { "$schema": "https://playground.wordpress.net/blueprint-schema.json", "login": true, "siteOptions": { "blogname": "My first Blueprint" }, "plugins": ["hello-dolly", "https://raw.githubusercontent.com/wordpress/blueprints/trunk/docs/assets/hello-from-the-dashboard.zip"], "steps": [ { "step": "installTheme", "themeData": { "resource": "wordpress.org/themes", "slug": "adventurer" } } ] } ``` [   Run Blueprint   ](https://playground.wordpress.net/#eyIkc2NoZW1hIjoiaHR0cHM6Ly9wbGF5Z3JvdW5kLndvcmRwcmVzcy5uZXQvYmx1ZXByaW50LXNjaGVtYS5qc29uIiwibG9naW4iOnRydWUsInNpdGVPcHRpb25zIjp7ImJsb2duYW1lIjoiTXkgZmlyc3QgQmx1ZXByaW50In0sInBsdWdpbnMiOlsiaGVsbG8tZG9sbHkiLCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYWRhbXppZWwvYmx1ZXByaW50cy90cnVuay9kb2NzL2hlbGxvLW9uLXRoZS1kYXNoYm9hcmQuemlwIl0sInN0ZXBzIjpbeyJzdGVwIjoiaW5zdGFsbFRoZW1lIiwidGhlbWVaaXBGaWxlIjp7InJlc291cmNlIjoid29yZHByZXNzLm9yZy90aGVtZXMiLCJzbHVnIjoiYWR2ZW50dXJlciJ9fV19) ## 6. Change the site content Finally, let's delete the default content of the site and import a new one from a WordPress export file (WXR). ### Delete the old content There isn't a Blueprint step to delete the default content, but you can do that with a snippet of PHP code: ```php -1, 'post_type' => array('post', 'page'), 'post_status' => 'any' )); foreach ($posts as $post) { wp_delete_post($post->ID, true); } ``` To run that code during the site setup, use the [`runPHP` step](/blueprints/steps#RunPHPStep): ```json { // ... "steps": [ // ... { "step": "runPHP", "code": " -1,\n 'post_type' => array('post', 'page'),\n 'post_status' => 'any'\n));\n\nforeach ($posts as $post) {\n wp_delete_post($post->ID, true);\n}" } ] } ``` ### Import the new content Let's use the [`importWxr` step](/blueprints/steps#ImportWXRStep) to import a WordPress export (`WXR`) file that helps test WordPress themes. The file is available in the [WordPress/theme-test-data](https://github.com/WordPress/theme-test-data) repository, and you can access it via its `raw.githubusercontent.com` address: [https://raw.githubusercontent.com/WordPress/theme-test-data/master/themeunittestdata.wordpress.xml](https://raw.githubusercontent.com/WordPress/theme-test-data/master/themeunittestdata.wordpress.xml). Here's what the final Blueprint looks like: ```json { "$schema": "https://playground.wordpress.net/blueprint-schema.json", "login": true, "siteOptions": { "blogname": "My first Blueprint" }, "plugins": ["hello-dolly", "https://raw.githubusercontent.com/wordpress/blueprints/trunk/docs/assets/hello-from-the-dashboard.zip"], "steps": [ { "step": "installTheme", "themeData": { "resource": "wordpress.org/themes", "slug": "adventurer" } }, { "step": "runPHP", "code": " -1,\n 'post_type' => array('post', 'page'),\n 'post_status' => 'any'\n));\n\nforeach ($posts as $post) {\n wp_delete_post($post->ID, true);\n}" }, { "step": "importWxr", "file": { "resource": "url", "url": "https://raw.githubusercontent.com/WordPress/theme-test-data/master/themeunittestdata.wordpress.xml" } } ] } ``` [   Run Blueprint   ](https://playground.wordpress.net/#eyIkc2NoZW1hIjoiaHR0cHM6Ly9wbGF5Z3JvdW5kLndvcmRwcmVzcy5uZXQvYmx1ZXByaW50LXNjaGVtYS5qc29uIiwibG9naW4iOnRydWUsInNpdGVPcHRpb25zIjp7ImJsb2duYW1lIjoiTXkgZmlyc3QgQmx1ZXByaW50In0sInBsdWdpbnMiOlsiaGVsbG8tZG9sbHkiLCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYWRhbXppZWwvYmx1ZXByaW50cy90cnVuay9kb2NzL2Fzc2V0cy9oZWxsby1mcm9tLXRoZS1kYXNoYm9hcmQuemlwIl0sInN0ZXBzIjpbeyJzdGVwIjoiaW5zdGFsbFRoZW1lIiwidGhlbWVaaXBGaWxlIjp7InJlc291cmNlIjoid29yZHByZXNzLm9yZy90aGVtZXMiLCJzbHVnIjoiYWR2ZW50dXJlciJ9fSx7InN0ZXAiOiJydW5QSFAiLCJjb2RlIjoiPD9waHBcbnJlcXVpcmUgJy93b3JkcHJlc3Mvd3AtbG9hZC5waHAnO1xuXG4kcG9zdHMgPSBnZXRfcG9zdHMoYXJyYXkoXG4gICAgJ251bWJlcnBvc3RzJyA9PiAtMSxcbiAgICAncG9zdF90eXBlJyA9PiBhcnJheSgncG9zdCcsICdwYWdlJyksXG4gICAgJ3Bvc3Rfc3RhdHVzJyA9PiAnYW55J1xuKSk7XG5cbmZvcmVhY2ggKCRwb3N0cyBhcyAkcG9zdCkge1xuICAgIHdwX2RlbGV0ZV9wb3N0KCRwb3N0LT5JRCwgdHJ1ZSk7XG59In0seyJzdGVwIjoiaW1wb3J0V3hyIiwiZmlsZSI6eyJyZXNvdXJjZSI6InVybCIsInVybCI6Imh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9Xb3JkUHJlc3MvdGhlbWUtdGVzdC1kYXRhL21hc3Rlci90aGVtZXVuaXR0ZXN0ZGF0YS53b3JkcHJlc3MueG1sIn19XX0=) And that's it. Congratulations on creating your first Blueprint! 🥳