Importing content into WordPress with Blueprints
A new WordPress Playground site starts with basic content. A Blueprint can help fill that gap from a WordPress export file, generate content with WordPress APIs, or restore a Playground ZIP snapshot.
This guide focuses on choosing a Blueprint content import method and presents a small benchmark comparing three approaches. Importing data can be useful for theme starter content, test fixtures, educational content, and other demo-building strategies. For more options, see Providing content for your demo with Playground.
These approaches solve different problems:
| Method | Best for | What it moves |
|---|---|---|
importWxr | Portable content shared between WordPress sites without overwriting existing content | Posts, pages, custom post types, terms, authors, comments, and attachment references |
runPHP | Small, deterministic fixtures and content that needs custom logic | Anything the PHP code creates through WordPress APIs |
importWordPressFiles | Restoring a complete Playground demo | The database and any WordPress files present in the ZIP, such as plugins, themes, and uploads |
If you only need posts and terms, start with WXR. If the content is generated or
depends on setup logic, use runPHP. If the database, media, plugins, themes,
and settings must be restored together, use a ZIP snapshot.
Import a WordPress XML export with importWxr
WordPress calls its XML export format WordPress eXtended RSS, or WXR. Create one
from Tools > Export in an existing WordPress site, then pass it to the
importWxr step.
This example expects content.xml next to blueprint.json in a
Blueprint bundle:
{
"$schema": "https://playground.wordpress.net/blueprint-schema.json",
"landingPage": "/wp-admin/edit.php",
"preferredVersions": {
"php": "8.3",
"wp": "latest"
},
"login": true,
"steps": [
{
"step": "importWxr",
"file": {
"resource": "bundled",
"path": "/content.xml"
},
"fetchAttachments": false,
"rewriteUrls": true,
"importComments": false,
"authorsMode": "default-author",
"defaultAuthorUsername": "admin"
}
]
}
For a hosted file, replace the bundled resource with a
url resource. Set
fetchAttachments to true when the importer must download the media files
referenced by the WXR file. Network transfer can dominate the total setup time,
so the benchmark later in this guide disables attachment fetching.
importWxr can also map imported authors to local users, create users, and
apply explicit URL replacements. See the
importWxr options before importing content from
another domain.
Pros
- Uses WordPress's standard, portable content exchange format.
- Preserves post types, taxonomies, post meta, authors, comments, and content relationships represented in the export.
- Can fetch attachments and rewrite old content URLs for the new site.
- Keeps content separate from the destination's WordPress core, plugins, themes, and most site settings.
- Is easy to inspect, version, and edit manually in any text editor because the source is XML.
Cons
- Does not clone the whole site. Plugins, themes, plugin tables, options, and files that are not represented in WXR need separate Blueprint steps.
- Attachment imports require network access and depend on the old media URLs remaining available.
- Large XML files use more parsing time and memory than restoring an existing SQLite database.
- Re-importing is additive, not a synchronization. The importer creates terms it cannot find and never removes existing content; it only skips a post when an existing one matches its title, author, and date.
- The Blueprint installs the WordPress Importer dependency automatically, which adds setup work before the WXR import starts.
Generate content with runPHP
The runPHP step can call WordPress APIs directly.
Always load /wordpress/wp-load.php before calling functions such as
wp_insert_post().
The following example creates the 100 posts used by the benchmark:
{
"$schema": "https://playground.wordpress.net/blueprint-schema.json",
"landingPage": "/wp-admin/edit.php",
"preferredVersions": {
"php": "8.3",
"wp": "latest"
},
"login": true,
"steps": [
{
"step": "runPHP",
"code": "<?php\nrequire_once '/wordpress/wp-load.php';\n\nfor ( $index = 1; $index <= 100; $index++ ) {\n\t$result = wp_insert_post(\n\t\tarray(\n\t\t\t'post_title' => 'Benchmark post ' . $index,\n\t\t\t'post_content' => '<!-- wp:paragraph --><p>Blueprint import benchmark content.</p><!-- /wp:paragraph -->',\n\t\t\t'post_status' => 'publish',\n\t\t\t'post_type' => 'post',\n\t\t),\n\t\ttrue\n\t);\n\tif ( is_wp_error( $result ) ) {\n\t\tthrow new RuntimeException( $result->get_error_message() );\n\t}\n}"
}
]
}
For larger programs, keep the code in a small plugin or split the setup across
focused steps instead of maintaining a very long JSON string. Use WordPress
APIs rather than writing directly to database tables so hooks, caches, and data
validation still run. Keep in mind that the code is not limited to content:
runPHP can do anything PHP can, including overwriting options or modifying
the database directly.
Pros
- Provides complete control over the generated data and its relationships.
- Requires no content file and no network access.
- Can configure options, post meta, users, taxonomies, and plugin-specific data in the same operation.
- Works well for small test fixtures whose source of truth should remain code.
- Can be made idempotent by looking up existing records before creating them.
Cons
- A Blueprint is trusted input. Only run PHP from a source you trust.
- The script must handle errors, reruns, dependencies, and partial failures.
- Code can become coupled to a plugin's APIs or database model.
- Creating many records one at a time still runs WordPress hooks and database writes for every record, so performance degrades as the dataset grows.
- Custom PHP is more verbose than exporting existing editorial content.
Restore a Playground ZIP with importWordPressFiles
The importWordPressFiles step is a
site restore, not a content-only import. It unpacks top-level WordPress files
from a ZIP and replaces the corresponding paths in the new instance. A ZIP
created with Playground's Download as zip option includes wp-content, its
SQLite database, uploads, and a manifest used to adjust Playground scope URLs.
Place the downloaded file next to blueprint.json and reference it as a bundled
resource:
{
"$schema": "https://playground.wordpress.net/blueprint-schema.json",
"landingPage": "/",
"preferredVersions": {
"php": "8.3",
"wp": "latest"
},
"login": true,
"steps": [
{
"step": "importWordPressFiles",
"wordPressFilesZip": {
"resource": "bundled",
"path": "/site.zip"
}
}
]
}
The ZIP may contain wp-content only, a complete WordPress directory, or a
WordPress directory nested inside a wrapper folder. Use pathInZip when the
archive contains several sites or when automatic root detection is not enough.
Keep the source and destination WordPress, PHP, theme, and plugin versions compatible. The step upgrades the imported database and adjusts Playground scope URLs, but it is not a general migration tool for arbitrary production server configurations.
Pros
- Restores the database, uploads, plugins, themes, and settings together.
- Preserves plugin-specific tables and options that WXR cannot represent.
- Avoids recreating every post through WordPress APIs, which can be efficient for a prepared, repeatable demo.
- Works offline when the snapshot is bundled with the Blueprint.
- Produces the closest copy of the source Playground instance.
Cons
- Replaces any top-level paths present in the ZIP, so it can overwrite existing site state rather than merge content into it.
- Is larger, opaque, and harder to review or resolve in version-control merges.
- Couples the snapshot to its WordPress, PHP, theme, plugin, and database versions more tightly than WXR.
- Is unsuitable for importing selected posts into an existing site.
- Must only be restored from a trusted source; it can contain executable PHP and an entire database.
Test results for the different methods
The following test results were measured on July 13, 2026, on an Apple M4 Pro with 24 GB of memory. It used Node.js 22.16, WordPress 7.0, PHP 8.3, 100 posts, and five fresh-site rounds per method:
| Method | Input size | Median | Minimum | Maximum | Relative to fastest |
|---|---|---|---|---|---|
XML / importWxr | 106.0 KiB | 2.21 s | 2.16 s | 2.25 s | 6.90x |
PHP / runPHP | Generated code | 2.78 s | 2.76 s | 2.80 s | 8.68x |
ZIP / importWordPressFiles | 50.5 KiB | 320 ms | 318 ms | 322 ms | 1.00x |
Treat these values as a reference from one machine, not a universal ranking.
The dataset shape matters: attachments favor a self-contained ZIP, complex
WordPress hooks can slow runPHP, and WXR's portability may be more important
than raw speed.
Other content sources
Blueprints also support importThemeStarterContent
for a theme's registered starter content and the
wp-cli step for commands such as
wp post generate. They are useful when the theme or WP-CLI command is already
the canonical source of the demo data, but they are outside this content import
benchmark.