LaunchPad’s Recipe System is fully extensible, allowing developers to create custom site templates tailored to specific industries, clients, or use cases. This comprehensive developer guide covers the recipe architecture, JSON format, PHP hooks, and best practices.
Understanding the Recipe Architecture
A recipe is a site template blueprint that defines:
- Site type and category
- Recommended theme(s)
- Essential plugins
- Page structure and content
- Menu navigation
- Branding options (colors, fonts)
- Custom form fields for data collection
Recipes combine JSON data files with PHP filters to create repeatable, automated site configurations.
Use Cases for Custom Recipes:
- Industry-specific templates (restaurants, law firms, medical practices)
- Client-specific standardized deployments
- Niche vertical markets
- White-label agency offerings
- Multi-site network templates
Learn about WordPress template hierarchy.
Recipe Storage Locations
LaunchPad looks for recipes in three locations (in priority order):
1. Plugin Recipe Directory (Core Recipes)
/wp-content/plugins/LaunchPad/recipes/
├── blog.json
├── business.json
└── portfolio.json
Use for: Core plugin recipes (not recommended to modify directly)
2. Theme Recipe Directory (Theme-Specific)
/wp-content/themes/your-theme/LaunchPad-recipes/
└── custom-recipe.json
Use for: Theme-bundled recipes that require specific theme features
3. PHP Filter (Dynamic Recipes)
add_filter( 'LaunchPad_custom_recipes', function( $recipes ) {
$recipes['my-recipe'] = array( /* recipe data */ );
return $recipes;
});
Use for: Programmatically generated recipes, plugin-provided recipes, or mu-plugin recipes
Recipe JSON Format
Here’s the complete structure of a recipe JSON file:
{
"site_type": "business",
"name": "Business Website",
"description": "Professional business site with services, testimonials, and contact forms",
"icon": "dashicons-building",
"category": "business",
"pro_only": false,
"theme": "LaunchPad-bundle",
"theme_options": [
{
"slug": "LaunchPad-bundle",
"name": "LaunchPad Bundle",
"description": "Professional bundled theme with 10 homepage sections",
"recommended": true,
"bundled": true,
"preview_url": "",
"rating": 5.0,
"active_installs": 0
}
],
"plugins": ["contact-form-7", "wordpress-seo"],
"pages": ["home", "about", "services", "contact"],
"menu_structure": [
{
"title": "Home",
"page": "home",
"order": 1
},
{
"title": "About",
"page": "about",
"order": 2
},
{
"title": "Services",
"page": "services",
"order": 3
},
{
"title": "Contact",
"page": "contact",
"order": 4
}
],
"branding": {
"suggested_colors": ["#2563EB", "#1E40AF", "#60A5FA"],
"font_pairs": [
{
"heading": "Inter",
"body": "Inter"
},
{
"heading": "Playfair Display",
"body": "Lato"
}
]
},
"branding_fields": [
{
"id": "business_description",
"label": "Business Description",
"type": "textarea",
"placeholder": "Describe what your business does...",
"required": true,
"rows": 3,
"ai_hint": "Generate a compelling business description based on this input"
}
]
}
Recipe Field Reference
Top-Level Fields
site_type (string, required)
- Unique identifier for the recipe
- Lowercase, alphanumeric with hyphens only
- Example:
"blog","business","restaurant"
name (string, required)
- Display name shown in wizard
- Human-readable, 2-4 words
- Example:
"Restaurant Website"
description (string, required)
- Brief description of what the recipe creates
- 1-2 sentences
- Shown in recipe selection card
- Example:
"Professional restaurant site with menu, reservations, and online ordering"
icon (string, required)
- WordPress Dashicon class
- Format:
"dashicons-{icon-name}" - Example:
"dashicons-food"
category (string, required)
- Recipe category for filtering/grouping
- Options:
"blog","business","portfolio","ecommerce","nonprofit","other"
pro_only (boolean, required)
- Whether recipe requires Pro license
true= Only available to Pro usersfalse= Available to all users
theme (string, required)
- Default/recommended theme slug
- Must be valid WordPress.org theme slug or bundled theme
- Example:
"LaunchPad-bundle","twentytwentyfour"
theme_options Array
Array of theme objects representing available theme choices:
{
"slug": "LaunchPad-bundle",
"name": "LaunchPad Bundle",
"description": "Professional theme with advanced customizer controls",
"recommended": true,
"bundled": true,
"preview_url": "https://example.com/demo",
"rating": 5.0,
"active_installs": 1000
}
Fields:
- slug (string, required) – Theme directory name
- name (string, required) – Display name
- description (string, required) – Short description
- recommended (boolean) – Show “Recommended” badge
- bundled (boolean) – Included with plugin
- preview_url (string) – Live demo URL
- rating (float) – Star rating (0-5)
- active_installs (int) – Number of active installations
plugins Array
Array of plugin slugs to recommend/install:
"plugins": [
"contact-form-7",
"wordpress-seo",
"jetpack",
"wordfence"
]
Format:
- WordPress.org plugin directory slugs only
- Lowercase with hyphens
- Must be exact match to WordPress.org slug
- Max 10 plugins recommended per recipe
Validation:
- LaunchPad validates plugin existence via WordPress.org API
- Invalid slugs logged as warnings
- Missing plugins skipped during installation
pages Array
Array of page identifiers to generate:
"pages": [
"home",
"about",
"services",
"team",
"contact",
"privacy-policy"
]
Standard Page IDs:
"home"– Homepage with template"about"– About page"services"– Services overview"contact"– Contact page with form"blog"– Blog listing page"portfolio"– Portfolio page"privacy-policy"– Privacy policy"terms"– Terms and conditions
Custom Pages: You can define custom page IDs and provide content templates via PHP filter (see below).
menu_structure Array
Defines navigation menu structure:
"menu_structure": [
{
"title": "Home",
"page": "home",
"order": 1,
"parent": 0
},
{
"title": "Services",
"page": "services",
"order": 2,
"parent": 0,
"children": [
{
"title": "Web Design",
"page": "web-design",
"order": 1
}
]
}
]
Fields:
- title (string, required) – Menu item text
- page (string, required) – Page ID from pages array
- order (int) – Menu position (1-based)
- parent (int) – Parent menu item ID (0 = top level)
- children (array) – Nested submenu items
branding Object
Suggested branding options:
"branding": {
"suggested_colors": [
"#2563EB",
"#1E40AF",
"#60A5FA"
],
"font_pairs": [
{
"heading": "Inter",
"body": "Inter"
}
]
}
suggested_colors (array)
- Array of hex color codes
- First color = primary
- Second color = primary variant
- Third color = accent
font_pairs (array)
- Array of font pairing objects
- heading (string) – Google Font name for headings
- body (string) – Google Font name for body text
branding_fields Array
Custom form fields for data collection:
"branding_fields": [
{
"id": "business_name",
"label": "Business Name",
"type": "text",
"placeholder": "Enter your business name",
"required": true,
"default": "",
"ai_hint": "Use this as the main business name throughout content"
}
]
Field Types:
"text"– Single-line text input"textarea"– Multi-line text area"email"– Email address input"url"– URL input with validation"tel"– Phone number input"number"– Numeric input"select"– Dropdown selection"checkbox"– Single checkbox"radio"– Radio button group
Field Attributes:
- id (string, required) – Unique field identifier (alphanumeric + underscores)
- label (string, required) – Field label text
- type (string, required) – Field type (see above)
- placeholder (string) – Placeholder text
- required (boolean) – Whether field is mandatory
- default (mixed) – Default value
- ai_hint (string) – Context for AI content generation (Pro)
- options (array) – Options for select/radio types
- min (int) – Minimum value for number type
- max (int) – Maximum value for number type
- rows (int) – Rows for textarea type
Example select field:
{
"id": "business_type",
"label": "Business Type",
"type": "select",
"required": true,
"options": [
{
"value": "retail",
"label": "Retail Store"
},
{
"value": "service",
"label": "Service Provider"
},
{
"value": "restaurant",
"label": "Restaurant"
}
],
"ai_hint": "Tailor content based on business type"
}
Creating a Custom Recipe via PHP
Instead of JSON files, you can register recipes programmatically:
<?php
/**
* Register custom restaurant recipe
*/
add_filter( 'LaunchPad_custom_recipes', function( $recipes ) {
$recipes['restaurant'] = array(
'site_type' => 'restaurant',
'name' => 'Restaurant Website',
'description' => 'Full-featured restaurant site with menu, reservations, and online ordering',
'icon' => 'dashicons-food',
'category' => 'business',
'pro_only' => false,
'theme' => 'LaunchPad-bundle',
'theme_options' => array(
array(
'slug' => 'LaunchPad-bundle',
'name' => 'LaunchPad Bundle',
'description' => 'Perfect for restaurants with menu sections',
'recommended' => true,
'bundled' => true,
),
),
'plugins' => array(
'contact-form-7', // Contact forms
'wordpress-seo', // SEO
'restaurant-reservations', // Booking system
'wp-google-maps', // Location map
),
'pages' => array(
'home',
'menu',
'about',
'reservations',
'contact',
'gallery',
),
'menu_structure' => array(
array(
'title' => 'Home',
'page' => 'home',
'order' => 1,
),
array(
'title' => 'Menu',
'page' => 'menu',
'order' => 2,
),
array(
'title' => 'Reserve',
'page' => 'reservations',
'order' => 3,
),
array(
'title' => 'About',
'page' => 'about',
'order' => 4,
),
array(
'title' => 'Contact',
'page' => 'contact',
'order' => 5,
),
),
'branding' => array(
'suggested_colors' => array(
'#DC2626', // Red
'#B91C1C', // Dark Red
'#FCA5A5', // Light Red
),
'font_pairs' => array(
array(
'heading' => 'Playfair Display',
'body' => 'Lato',
),
array(
'heading' => 'Merriweather',
'body' => 'Open Sans',
),
),
),
'branding_fields' => array(
array(
'id' => 'restaurant_name',
'label' => 'Restaurant Name',
'type' => 'text',
'placeholder' => 'Your Restaurant Name',
'required' => true,
'ai_hint' => 'Primary restaurant name for branding',
),
array(
'id' => 'cuisine_type',
'label' => 'Cuisine Type',
'type' => 'select',
'required' => true,
'options' => array(
array( 'value' => 'italian', 'label' => 'Italian' ),
array( 'value' => 'asian', 'label' => 'Asian' ),
array( 'value' => 'mexican', 'label' => 'Mexican' ),
array( 'value' => 'american', 'label' => 'American' ),
array( 'value' => 'other', 'label' => 'Other' ),
),
'ai_hint' => 'Adjust content tone and vocabulary for cuisine type',
),
array(
'id' => 'restaurant_description',
'label' => 'Restaurant Description',
'type' => 'textarea',
'placeholder' => 'Describe your restaurant, atmosphere, and specialty...',
'required' => true,
'rows' => 4,
'ai_hint' => 'Use this description to generate personalized hero and about content',
),
array(
'id' => 'phone_number',
'label' => 'Phone Number',
'type' => 'tel',
'placeholder' => '(555) 123-4567',
'required' => true,
),
array(
'id' => 'address',
'label' => 'Restaurant Address',
'type' => 'textarea',
'placeholder' => '123 Main St, City, ST 12345',
'required' => true,
'rows' => 2,
),
),
);
return $recipes;
});
When to use PHP vs. JSON:
- JSON: Simple, static recipes
- PHP: Dynamic recipes, conditional logic, complex customization
Custom Page Content Templates
Define custom content for your recipe pages:
<?php
/**
* Provide custom page content for restaurant recipe
*/
add_filter( 'LaunchPad_page_content_template', function( $content, $page_id, $recipe_data ) {
// Only for restaurant recipe
if ( $recipe_data['site_type'] !== 'restaurant' ) {
return $content;
}
// Get user-provided branding data
$restaurant_name = isset( $recipe_data['branding']['restaurant_name'] )
? $recipe_data['branding']['restaurant_name']
: 'Our Restaurant';
$description = isset( $recipe_data['branding']['restaurant_description'] )
? $recipe_data['branding']['restaurant_description']
: 'Welcome to our restaurant.';
// Custom content for menu page
if ( $page_id === 'menu' ) {
$content = '
<!-- wp:heading {"level":1} -->
<h1>' . esc_html( $restaurant_name ) . ' Menu</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>' . esc_html( $description ) . '</p>
<!-- /wp:paragraph -->
<!-- wp:heading {"level":2} -->
<h2>Appetizers</h2>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
<li>Sample Appetizer 1 - $10</li>
<li>Sample Appetizer 2 - $12</li>
<li>Sample Appetizer 3 - $8</li>
</ul>
<!-- /wp:list -->
<!-- wp:heading {"level":2} -->
<h2>Main Courses</h2>
<!-- /wp:heading -->
<!-- wp:list -->
<ul>
<li>Sample Entree 1 - $25</li>
<li>Sample Entree 2 - $28</li>
<li>Sample Entree 3 - $22</li>
</ul>
<!-- /wp:list -->
';
}
// Custom content for reservations page
if ( $page_id === 'reservations' ) {
$content = '
<!-- wp:heading {"level":1} -->
<h1>Make a Reservation</h1>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>Reserve your table at ' . esc_html( $restaurant_name ) . ' today!</p>
<!-- /wp:paragraph -->
<!-- wp:shortcode -->
Error: Contact form not found.
<!– /wp:shortcode –> ‘; } return $content; }, 10, 3 );
Note: Content must use Gutenberg block markup.
Modifying Existing Recipes
You can modify core recipes without editing plugin files:
<?php
/**
* Modify the Business recipe to add WooCommerce plugin
*/
add_filter( 'LaunchPad_recipe_data', function( $recipe_data ) {
// Only modify business recipe
if ( $recipe_data['site_type'] !== 'business' ) {
return $recipe_data;
}
// Add WooCommerce to plugins list
if ( ! in_array( 'woocommerce', $recipe_data['plugins'], true ) ) {
$recipe_data['plugins'][] = 'woocommerce';
}
// Add Shop page
if ( ! in_array( 'shop', $recipe_data['pages'], true ) ) {
$recipe_data['pages'][] = 'shop';
}
// Add Shop to menu
$recipe_data['menu_structure'][] = array(
'title' => 'Shop',
'page' => 'shop',
'order' => 5,
);
return $recipe_data;
}, 10, 1 );
Pro-Only Recipes
Create recipes that require a Pro license:
<?php
add_filter( 'LaunchPad_additional_pro_recipes', function( $pro_recipes ) {
$pro_recipes['medical-practice'] = array(
'site_type' => 'medical-practice',
'name' => 'Medical Practice',
'description' => 'HIPAA-compliant medical practice site with appointments',
'icon' => 'dashicons-heart',
'category' => 'business',
'pro_only' => true, // Only available to Pro users
// ... rest of recipe configuration
);
return $pro_recipes;
});
Pro-only features:
- Browse WordPress.org themes/plugins
- Custom colors and fonts
- AI content generation
- Additional image uploads
- Empty site option
Validation and Error Handling
LaunchPad validates recipes on load:
Validation Checks:
- Required fields present
- Valid field types
- Valid page IDs
- Valid plugin slugs (via WP.org API)
- Valid theme slugs
- Proper array structures
- Valid color hex codes
- Valid Google Font names
Handling Validation Errors:
<?php
/**
* Log validation errors for debugging
*/
add_action( 'LaunchPad_recipe_validation_error', function( $recipe_slug, $errors ) {
error_log( sprintf(
'Recipe validation failed for "%s": %s',
$recipe_slug,
implode( ', ', $errors )
) );
}, 10, 2 );
Best Practices
1. Keep Plugins Minimal
- Max 5-6 plugins per recipe
- Only include essential plugins
- Avoid conflicting plugins
- Test plugin combinations
2. Provide Meaningful Branding Fields
- Collect data you’ll actually use
- Make labels clear and specific
- Include ai_hint for Pro users
- Set reasonable required/optional balance
3. Create Realistic Content Templates
- Use authentic placeholder content
- Include proper Gutenberg blocks
- Make content easy to edit
- Provide helpful comments
4. Test Thoroughly
- Test recipe installation multiple times
- Verify all plugins install correctly
- Check page content renders properly
- Validate menu structure
- Test on fresh WordPress installs
5. Document Your Recipes
- Include README explaining purpose
- Document custom branding fields
- Provide usage examples
- List any prerequisites
6. Version Your Recipes
- Add version numbers to custom recipes
- Track changes in changelog
- Test backwards compatibility
- Update documentation
Troubleshooting
Recipe doesn’t appear in wizard
Causes:
- JSON syntax error
- Invalid recipe structure
- Duplicate site_type
- File permissions
Solutions:
- Validate JSON with JSONLint
- Check error logs:
wp-content/debug.log - Ensure site_type is unique
- Check file permissions (644)
Pages created but content is empty
Causes:
- Page ID not in
LaunchPad_page_content_templatefilter - Invalid Gutenberg block markup
- Content filter not firing
Solutions:
- Add content template filter
- Validate block markup
- Check filter priority
Plugins fail to install
Causes:
- Invalid plugin slug
- Plugin not on WordPress.org
- Server timeout
- Permission issues
Solutions:
- Verify plugin slugs on WordPress.org
- Use exact directory name
- Increase timeout in
wp-config.php - Check server write permissions
Recipe Distribution
Share your custom recipes:
1. As a Plugin
Create a mu-plugin or regular plugin that registers recipes via filters.
2. As a Theme Feature
Bundle recipes with your theme in /LaunchPad-recipes/ directory.
3. As JSON Files
Distribute JSON files for users to add manually.
4. Via GitHub
Host recipes on GitHub for version control and collaboration.
Conclusion
Custom recipes extend LaunchPad’s power to create industry-specific, repeatable site templates. By leveraging the recipe JSON format and PHP hooks, developers can build sophisticated site creation workflows for clients, agencies, or product offerings.
For more information on LaunchPad development, see LaunchPad GitHub repository or explore WordPress Plugin Developer Handbook.
Ready to create recipes? Start with the examples above and build your

