Client wants: 50 landing pages with same layout, different cities
Manual method: 50 × 15 minutes = 12.5 hours clicking in Elementor!!
My response: "Give me 45 minutes to write a script"
Result:
- Script runs: 3 minutes
- 50 pages created: Identical layouts, unique content
- JSON config drives everything
- Zero clicking!!
Here's how to automate page builder layout generation programmatically:
Why Automate Page Builder Layouts?
Manual page building SUCKS when:
- Creating 50+ similar pages (locations, products, services)
- Client wants consistent layouts across all pages
- Content changes frequently but structure stays same
- Testing A/B variations of layouts
- Migrating layouts between sites
Automation = sanity!!
Architecture: JSON Config → Script → Page Builder
Three components:
- JSON configuration (layout structure + content variables)
- PHP script (reads JSON, generates page builder meta)
- WP-CLI command (creates pages programmatically)
Workflow:
cities.json → automation-script.php → wp-cli → 50 pages created
Supported Page Builders
This technique works with:
- Elementor (stores layouts in postmeta
_elementor_data) - Divi (stores layouts in
_et_pb_page_layout) - Beaver Builder (stores layouts in
_fl_builder_data) - WPBakery (stores layouts as shortcodes in post_content)
- Oxygen (stores layouts in postmeta
ct_builder_shortcodes)
Key insight: All page builders store layouts as JSON or shortcodes in database!!
Example 1: Elementor Automation
Step 1: Export Template as JSON
Create one perfect page manually in Elementor
Export template:
Elementor → Tools → Export Template
Saved as: template.json
Structure:
{
"content": [
{
"id": "abc123",
"elType": "section",
"elements": [
{
"elType": "column",
"elements": [
{
"elType": "widget",
"widgetType": "heading",
"settings": {
"title": "{{CITY_NAME}} Services"
}
}
]
}
]
}
]
}
Step 2: Create City Data JSON
File: cities.json
[
{
"city": "Chicago",
"state": "IL",
"zip": "60601",
"phone": "(312) 555-0100"
},
{
"city": "Boston",
"state": "MA",
"zip": "02108",
"phone": "(617) 555-0200"
},
{
"city": "Seattle",
"state": "WA",
"zip": "98101",
"phone": "(206) 555-0300"
}
]
Step 3: PHP Automation Script
File: generate-elementor-pages.php
<?php
/**
* Elementor Page Generator
* Usage: php generate-elementor-pages.php
*/
// Load WordPress
require_once('/path/to/wp-load.php');
// Load data
$template = json_decode(file_get_contents('template.json'), true);
$cities = json_decode(file_get_contents('cities.json'), true);
foreach ($cities as $city) {
// Replace placeholders in template
$layout = replace_placeholders($template, [
'{{CITY_NAME}}' => $city['city'],
'{{STATE}}' => $city['state'],
'{{ZIP}}' => $city['zip'],
'{{PHONE}}' => $city['phone']
]);
// Create page
$page_id = wp_insert_post([
'post_title' => $city['city'] . ' Services',
'post_status' => 'publish',
'post_type' => 'page',
'post_content' => '' // Elementor uses meta, not content
]);
if (!is_wp_error($page_id)) {
// Save Elementor data
update_post_meta($page_id, '_elementor_data', wp_slash(json_encode($layout['content'])));
update_post_meta($page_id, '_elementor_edit_mode', 'builder');
update_post_meta($page_id, '_elementor_template_type', 'wp-page');
update_post_meta($page_id, '_elementor_version', '3.16.0');
echo "✅ Created: {$city['city']} (ID: {$page_id})\n";
}
}
function replace_placeholders($data, $replacements) {
$json = json_encode($data);
foreach ($replacements as $placeholder => $value) {
$json = str_replace($placeholder, $value, $json);
}
return json_decode($json, true);
}
echo "\n✨ Generation complete!\n";
Step 4: Run Script
php generate-elementor-pages.php
Output:
✅ Created: Chicago (ID: 123)
✅ Created: Boston (ID: 124)
✅ Created: Seattle (ID: 125)
✨ Generation complete!
3 pages created in seconds!!
Example 2: Divi Layout Automation
Divi Storage Format
Divi stores layouts as JSON in _et_pb_page_layout postmeta
Sample Divi JSON:
[
{
"type": "et_pb_section",
"attrs": {
"background_color": "#ffffff"
},
"content": [
{
"type": "et_pb_row",
"content": [
{
"type": "et_pb_column",
"content": [
{
"type": "et_pb_text",
"attrs": {
"content": "<h1>{{CITY_NAME}} Professional Services</h1>"
}
}
]
}
]
}
]
}
]
Divi Automation Script
<?php
require_once('/path/to/wp-load.php');
$template = json_decode(file_get_contents('divi-template.json'), true);
$cities = json_decode(file_get_contents('cities.json'), true);
foreach ($cities as $city) {
$layout = replace_placeholders($template, [
'{{CITY_NAME}}' => $city['city'],
'{{STATE}}' => $city['state']
]);
$page_id = wp_insert_post([
'post_title' => $city['city'] . ' Services',
'post_status' => 'publish',
'post_type' => 'page'
]);
if (!is_wp_error($page_id)) {
// Divi-specific meta
update_post_meta($page_id, '_et_pb_use_builder', 'on');
update_post_meta($page_id, '_et_pb_page_layout', wp_slash(json_encode($layout)));
update_post_meta($page_id, '_et_pb_old_content', '');
echo "✅ Divi page: {$city['city']}\n";
}
}
Example 3: WP-CLI Integration
Create WP-CLI Command
File: wp-content/mu-plugins/page-generator.php
<?php
/**
* Plugin Name: Page Generator CLI
*/
if (defined('WP_CLI') && WP_CLI) {
WP_CLI::add_command('generate pages', 'Page_Generator_CLI');
}
class Page_Generator_CLI {
/**
* Generate pages from template
*
* ## OPTIONS
*
* --template=<file>
* : Template JSON file
*
* --data=<file>
* : Data JSON file
*
* --builder=<name>
* : Page builder (elementor|divi|beaver)
*
* ## EXAMPLES
*
* wp generate pages --template=template.json --data=cities.json --builder=elementor
*/
public function __invoke($args, $assoc_args) {
$template_file = $assoc_args['template'];
$data_file = $assoc_args['data'];
$builder = $assoc_args['builder'];
if (!file_exists($template_file)) {
WP_CLI::error("Template file not found: {$template_file}");
}
if (!file_exists($data_file)) {
WP_CLI::error("Data file not found: {$data_file}");
}
$template = json_decode(file_get_contents($template_file), true);
$data = json_decode(file_get_contents($data_file), true);
$generated = 0;
foreach ($data as $item) {
$page_id = $this->create_page($template, $item, $builder);
if ($page_id) {
WP_CLI::success("Created page ID {$page_id}: {$item['city']}");
$generated++;
}
}
WP_CLI::success("Generated {$generated} pages!");
}
private function create_page($template, $data, $builder) {
// Replace placeholders
$layout = $this->replace_placeholders($template, $data);
// Create page
$page_id = wp_insert_post([
'post_title' => $data['city'] . ' Services',
'post_status' => 'publish',
'post_type' => 'page'
]);
if (is_wp_error($page_id)) {
return false;
}
// Save builder-specific meta
switch ($builder) {
case 'elementor':
update_post_meta($page_id, '_elementor_data', wp_slash(json_encode($layout)));
update_post_meta($page_id, '_elementor_edit_mode', 'builder');
break;
case 'divi':
update_post_meta($page_id, '_et_pb_use_builder', 'on');
update_post_meta($page_id, '_et_pb_page_layout', wp_slash(json_encode($layout)));
break;
case 'beaver':
update_post_meta($page_id, '_fl_builder_enabled', '1');
update_post_meta($page_id, '_fl_builder_data', $layout);
break;
}
return $page_id;
}
private function replace_placeholders($data, $replacements) {
$json = json_encode($data);
// Build replacement map
$search = [];
$replace = [];
foreach ($replacements as $key => $value) {
$search[] = '{{' . strtoupper($key) . '}}';
$replace[] = $value;
}
$json = str_replace($search, $replace, $json);
return json_decode($json, true);
}
}
Usage
# Generate 50 city pages
wp generate pages \
--template=elementor-template.json \
--data=cities.json \
--builder=elementor
# Output:
# Success: Created page ID 123: Chicago
# Success: Created page ID 124: Boston
# Success: Created page ID 125: Seattle
# Success: Generated 50 pages!
Advanced: REST API Integration
Create REST Endpoint
<?php
add_action('rest_api_init', function() {
register_rest_route('page-gen/v1', '/create', [
'methods' => 'POST',
'callback' => 'generate_page_via_api',
'permission_callback' => function() {
return current_user_can('edit_pages');
}
]);
});
function generate_page_via_api($request) {
$params = $request->get_json_params();
$template = $params['template'];
$data = $params['data'];
$builder = $params['builder'];
// Replace placeholders
$layout = replace_placeholders($template, $data);
// Create page
$page_id = wp_insert_post([
'post_title' => $data['title'],
'post_status' => 'publish',
'post_type' => 'page'
]);
// Save builder meta
if ($builder === 'elementor') {
update_post_meta($page_id, '_elementor_data', wp_slash(json_encode($layout)));
update_post_meta($page_id, '_elementor_edit_mode', 'builder');
}
return new WP_REST_Response([
'success' => true,
'page_id' => $page_id,
'url' => get_permalink($page_id)
], 201);
}
Call API from External Script
// Node.js example
const axios = require('axios');
async function generatePages() {
const template = require('./template.json');
const cities = require('./cities.json');
for (const city of cities) {
const response = await axios.post(
'https://yoursite.com/wp-json/page-gen/v1/create',
{
template: template,
data: {
title: `${city.city} Services`,
...city
},
builder: 'elementor'
},
{
auth: {
username: 'admin',
password: 'application-password'
}
}
);
console.log(`✅ Created: ${response.data.url}`);
}
}
generatePages();
Real-World Use Case: Agency Workflow
Client: National HVAC Company
Need: Landing page for every city (300+ locations)
Layout:
- Hero with city name
- Service list
- Local testimonials
- Contact form with city phone
- Map embed with city coordinates
Manual time: 300 × 20 minutes = 100 hours!!
Automated time:
- Template creation: 2 hours
- CSV data prep: 1 hour
- Script development: 3 hours
- Generation: 5 minutes
- Total: 6 hours = 94 hours saved!!
Implementation
Data file: hvac-locations.csv
city,state,zip,phone,lat,lng
Chicago,IL,60601,(312) 555-0100,41.8781,-87.6298
Boston,MA,02108,(617) 555-0200,42.3601,-71.0589
Script:
<?php
require_once('wp-load.php');
$template = json_decode(file_get_contents('hvac-template.json'), true);
$csv = array_map('str_getcsv', file('hvac-locations.csv'));
$headers = array_shift($csv);
foreach ($csv as $row) {
$location = array_combine($headers, $row);
$layout = replace_placeholders($template, [
'{{CITY}}' => $location['city'],
'{{STATE}}' => $location['state'],
'{{PHONE}}' => $location['phone'],
'{{LAT}}' => $location['lat'],
'{{LNG}}' => $location['lng']
]);
$page_id = wp_insert_post([
'post_title' => "HVAC Services in {$location['city']}, {$location['state']}",
'post_name' => sanitize_title("hvac-{$location['city']}-{$location['state']}"),
'post_status' => 'publish',
'post_type' => 'page'
]);
update_post_meta($page_id, '_elementor_data', wp_slash(json_encode($layout)));
update_post_meta($page_id, '_elementor_edit_mode', 'builder');
// SEO meta
update_post_meta($page_id, '_yoast_wpseo_title',
"HVAC Services {$location['city']}, {$location['state']} | Company Name");
update_post_meta($page_id, '_yoast_wpseo_metadesc',
"Professional HVAC services in {$location['city']}, {$location['state']}. Call {$location['phone']} today!");
echo "✅ {$location['city']}, {$location['state']}\n";
}
Result: 300 pages generated in 5 minutes!!
Handling Dynamic Content
ACF Integration
// Add ACF fields to generated page
update_field('service_area', $location['city'], $page_id);
update_field('phone_number', $location['phone'], $page_id);
update_field('map_coordinates', [
'lat' => $location['lat'],
'lng' => $location['lng']
], $page_id);
Dynamic Elementor Widgets
{
"elType": "widget",
"widgetType": "text-editor",
"settings": {
"editor": "[acf field='service_area']"
}
}
Elementor renders ACF shortcode dynamically!!
Error Handling & Logging
<?php
class Page_Generator {
private $log = [];
public function generate($template, $data) {
foreach ($data as $item) {
try {
$page_id = $this->create_page($template, $item);
$this->log[] = "✅ Success: {$item['city']} (ID: {$page_id})";
} catch (Exception $e) {
$this->log[] = "❌ Error: {$item['city']} - {$e->getMessage()}";
}
}
// Save log
file_put_contents('generation-log.txt', implode("\n", $this->log));
}
private function create_page($template, $item) {
$page_id = wp_insert_post([
'post_title' => $item['city'] . ' Services',
'post_status' => 'publish',
'post_type' => 'page'
]);
if (is_wp_error($page_id)) {
throw new Exception($page_id->get_error_message());
}
// Validate layout before saving
if (!$this->validate_layout($template)) {
throw new Exception('Invalid layout structure');
}
$layout = $this->replace_placeholders($template, $item);
update_post_meta($page_id, '_elementor_data', wp_slash(json_encode($layout)));
return $page_id;
}
private function validate_layout($layout) {
// Check required fields exist
return isset($layout['content']) && is_array($layout['content']);
}
}
Bottom Line
Stop clicking page builders for repetitive layouts!!
Automation wins:
- 100+ pages: Minutes not days
- Zero human error
- Consistent layouts guaranteed
- Easy bulk updates (regenerate all pages)
- Client thinks you're wizard!!
My typical workflow:
- Build one perfect template manually (1 hour)
- Export as JSON (30 seconds)
- Prepare data CSV/JSON (30 minutes)
- Write generation script (2 hours)
- Run script (2 minutes)
ROI: Pays for itself after 10 pages!!
For 50+ pages: Automation is NON-NEGOTIABLE!!
Page builder automation = agency superpower!! 🚀
This article contains affiliate links!


Top comments (1)
Used this exact workflow last month for client with 85 location pages... saved me like 3 DAYS of clicking around in Divi!! Template took 90 min to perfect, wrote the PHP script in 2 hours, then just ran it and boom - 85 pages with unique city content, phone numbers, map embeds, everything. Client was like "how did you do this so fast" lol... JSON config is KEY - once you nail the placeholder system you can pump out pages for ANY repetitive layout. Game changer for agency work honestly...