While Nexus Pro provides seven essential schema types out of the box, some projects require custom schema implementations—industry-specific types, additional properties, or nested schema structures. Creating custom schema types in WordPress requires understanding Schema.org vocabulary, JSON-LD format, and WordPress hooks for proper implementation.

This advanced guide shows you how to create custom schema types, extend existing schemas, implement complex nested structures, and validate your structured data for Google compatibility.
Understanding Schema.org Structure
Foundation knowledge for custom implementations.
Schema.org Hierarchy
Core Concept: Schema.org uses hierarchical types where specific types inherit properties from parent types.
Example Hierarchy:
Thing
└── CreativeWork
└── Article
└── NewsArticle
└── BlogPosting
└── TechArticle
Inheritance: NewsArticle inherits all properties from Article, CreativeWork, and Thing.
JSON-LD Format
Structure:
{
"@context": "https://schema.org",
"@type": "TypeName",
"property1": "value1",
"property2": "value2",
"nestedObject": {
"@type": "NestedType",
"nestedProperty": "value"
}
}
Key Elements:
@context: Schema.org vocabulary@type: Schema type- Properties: Type-specific attributes
- Values: Strings, numbers, objects, arrays
Common Schema Types
Available on Schema.org:
Organizations:
- Organization
- LocalBusiness
- Corporation
- EducationalOrganization
Creative Works:
- Article
- BlogPosting
- Book
- Movie
- MusicAlbum
Events:
- Event
- BusinessEvent
- SocialEvent
Products:
- Product
- SoftwareApplication
- Vehicle
People:
- Person
200+ types available: Check Schema.org for complete list.
Creating Basic Custom Schema
Implement custom schema from scratch.
Step 1: Choose Schema Type
Determine Type: Browse Schema.org to find appropriate type for your content.
Example: Creating SoftwareApplication schema for plugin documentation.
Step 2: Define Schema Structure
Plan Properties:
/**
* SoftwareApplication schema properties:
* - name: Application name
* - applicationCategory: Category (e.g., "DeveloperApplication")
* - operatingSystem: "WordPress"
* - offers: Price/availability
* - aggregateRating: User ratings
*/
Step 3: Create Schema Function
Implementation:
function custom_software_schema($post_id) {
// Get post data
$post = get_post($post_id);
// Build schema array
$schema = [
'@context' => 'https://schema.org',
'@type' => 'SoftwareApplication',
'name' => get_the_title($post_id),
'description' => get_the_excerpt($post_id),
'applicationCategory' => 'DeveloperApplication',
'operatingSystem' => 'WordPress',
'offers' => [
'@type' => 'Offer',
'price' => get_post_meta($post_id, 'price', true),
'priceCurrency' => 'USD',
'availability' => 'https://schema.org/InStock'
],
'aggregateRating' => [
'@type' => 'AggregateRating',
'ratingValue' => get_post_meta($post_id, 'rating', true),
'reviewCount' => get_post_meta($post_id, 'review_count', true)
]
];
return $schema;
}
Step 4: Output Schema
Add to WordPress:
function output_software_schema() {
if (is_singular('plugin')) {
$schema = custom_software_schema(get_the_ID());
echo '<script type="application/ld+json">' .
wp_json_encode($schema, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) .
'</script>';
}
}
add_action('wp_head', 'output_software_schema');
Output:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Nexus Pro",
...
}
</script>
Extending Existing Schema
Add properties to Nexus Pro schemas.
Filter Nexus Pro Schema
Hook Into Schema Output:
// Add custom properties to Article schema
add_filter('nexus_pro_article_schema', 'extend_article_schema', 10, 2);
function extend_article_schema($schema, $post_id) {
// Add wordCount property
$content = get_post_field('post_content', $post_id);
$word_count = str_word_count(strip_tags($content));
$schema['wordCount'] = $word_count;
// Add timeRequired for reading
$minutes = ceil($word_count / 200); // 200 words per minute
$schema['timeRequired'] = 'PT' . $minutes . 'M'; // ISO 8601 duration
// Add inLanguage
$schema['inLanguage'] = get_locale();
// Add audience
$difficulty = get_post_meta($post_id, 'difficulty_level', true);
if ($difficulty) {
$schema['audience'] = [
'@type' => 'EducationalAudience',
'educationalRole' => $difficulty // Beginner, Intermediate, Advanced
];
}
return $schema;
}
Add Nested Schemas
Complex Structures:
add_filter('nexus_pro_article_schema', 'add_citation_schema', 10, 2);
function add_citation_schema($schema, $post_id) {
// Get cited sources from custom field
$citations = get_post_meta($post_id, 'citations', true);
if (!empty($citations)) {
$schema['citation'] = [];
foreach ($citations as $citation) {
$schema['citation'][] = [
'@type' => 'CreativeWork',
'name' => $citation['title'],
'url' => $citation['url'],
'author' => [
'@type' => 'Person',
'name' => $citation['author']
]
];
}
}
return $schema;
}
Advanced Schema Patterns
Complex implementations.
Breadcrumb Schema
Navigation Structure:
function generate_breadcrumb_schema() {
if (!is_singular()) {
return;
}
$breadcrumbs = [];
$position = 1;
// Home
$breadcrumbs[] = [
'@type' => 'ListItem',
'position' => $position++,
'name' => 'Home',
'item' => home_url('/')
];
// Categories
$categories = get_the_category();
if ($categories) {
$category = $categories[0];
$breadcrumbs[] = [
'@type' => 'ListItem',
'position' => $position++,
'name' => $category->name,
'item' => get_category_link($category->term_id)
];
}
// Current post
$breadcrumbs[] = [
'@type' => 'ListItem',
'position' => $position,
'name' => get_the_title(),
'item' => get_permalink()
];
$schema = [
'@context' => 'https://schema.org',
'@type' => 'BreadcrumbList',
'itemListElement' => $breadcrumbs
];
return $schema;
}
Recipe Schema
Detailed Cooking Instructions:
function create_recipe_schema($post_id) {
$schema = [
'@context' => 'https://schema.org',
'@type' => 'Recipe',
'name' => get_the_title($post_id),
'image' => get_the_post_thumbnail_url($post_id, 'large'),
'author' => [
'@type' => 'Person',
'name' => get_the_author_meta('display_name')
],
'datePublished' => get_the_date('c', $post_id),
'description' => get_the_excerpt($post_id),
'prepTime' => 'PT' . get_post_meta($post_id, 'prep_time', true) . 'M',
'cookTime' => 'PT' . get_post_meta($post_id, 'cook_time', true) . 'M',
'totalTime' => 'PT' . get_post_meta($post_id, 'total_time', true) . 'M',
'recipeYield' => get_post_meta($post_id, 'servings', true),
'recipeCategory' => get_post_meta($post_id, 'category', true),
'recipeCuisine' => get_post_meta($post_id, 'cuisine', true),
'nutrition' => [
'@type' => 'NutritionInformation',
'calories' => get_post_meta($post_id, 'calories', true) . ' calories'
],
'recipeIngredient' => get_post_meta($post_id, 'ingredients', true), // Array
'recipeInstructions' => []
];
// Build instructions
$steps = get_post_meta($post_id, 'instructions', true);
foreach ($steps as $index => $step) {
$schema['recipeInstructions'][] = [
'@type' => 'HowToStep',
'name' => 'Step ' . ($index + 1),
'text' => $step,
'url' => get_permalink($post_id) . '#step-' . ($index + 1)
];
}
// Aggregate rating
$rating = get_post_meta($post_id, 'rating', true);
if ($rating) {
$schema['aggregateRating'] = [
'@type' => 'AggregateRating',
'ratingValue' => $rating,
'reviewCount' => get_post_meta($post_id, 'review_count', true)
];
}
return $schema;
}
Video Object Schema
YouTube/Vimeo Integration:
function create_video_schema($post_id) {
$video_url = get_post_meta($post_id, 'video_url', true);
if (empty($video_url)) {
return null;
}
$schema = [
'@context' => 'https://schema.org',
'@type' => 'VideoObject',
'name' => get_the_title($post_id),
'description' => get_the_excerpt($post_id),
'thumbnailUrl' => get_the_post_thumbnail_url($post_id, 'large'),
'uploadDate' => get_the_date('c', $post_id),
'duration' => 'PT' . get_post_meta($post_id, 'duration', true) . 'S', // seconds
'contentUrl' => $video_url,
'embedUrl' => $video_url,
'publisher' => [
'@type' => 'Organization',
'name' => get_bloginfo('name'),
'logo' => [
'@type' => 'ImageObject',
'url' => get_site_icon_url()
]
]
];
return $schema;
}
Multiple Schemas Per Page
Implement graph structure.
Schema Graph
Multiple Related Schemas:
function output_schema_graph() {
$graph = [
'@context' => 'https://schema.org',
'@graph' => []
];
// Add Organization schema
$graph['@graph'][] = [
'@type' => 'Organization',
'@id' => home_url('/#organization'),
'name' => get_bloginfo('name'),
'url' => home_url('/'),
'logo' => get_site_icon_url()
];
// Add WebSite schema
$graph['@graph'][] = [
'@type' => 'WebSite',
'@id' => home_url('/#website'),
'url' => home_url('/'),
'name' => get_bloginfo('name'),
'publisher' => [
'@id' => home_url('/#organization')
],
'potentialAction' => [
'@type' => 'SearchAction',
'target' => home_url('/?s={search_term_string}'),
'query-input' => 'required name=search_term_string'
]
];
// Add Article schema (if single post)
if (is_singular('post')) {
$graph['@graph'][] = create_article_schema(get_the_ID());
}
// Add Breadcrumb schema
$graph['@graph'][] = generate_breadcrumb_schema();
// Output
echo '<script type="application/ld+json">' .
wp_json_encode($graph, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) .
'</script>';
}
add_action('wp_head', 'output_schema_graph');
Benefits:
- All schemas in one script tag
- Clear entity relationships
- Efficient for Google parsing
Custom Meta Boxes for Schema
Admin interface for schema data.
Register Meta Box
function register_schema_meta_box() {
add_meta_box(
'custom_schema_meta',
'Schema Markup Data',
'render_schema_meta_box',
'post',
'side',
'default'
);
}
add_action('add_meta_boxes', 'register_schema_meta_box');
Render Fields
function render_schema_meta_box($post) {
wp_nonce_field('save_schema_meta', 'schema_meta_nonce');
$rating = get_post_meta($post->ID, 'rating', true);
$review_count = get_post_meta($post->ID, 'review_count', true);
$price = get_post_meta($post->ID, 'price', true);
?>
<p>
<label>Rating (1-5):</label><br>
<input type="number" name="rating" value="<?php echo esc_attr($rating); ?>"
min="1" max="5" step="0.1">
</p>
<p>
<label>Review Count:</label><br>
<input type="number" name="review_count" value="<?php echo esc_attr($review_count); ?>">
</p>
<p>
<label>Price ($):</label><br>
<input type="number" name="price" value="<?php echo esc_attr($price); ?>"
min="0" step="0.01">
</p>
<?php
}
Save Meta Data
function save_schema_meta($post_id) {
// Security checks
if (!isset($_POST['schema_meta_nonce']) ||
!wp_verify_nonce($_POST['schema_meta_nonce'], 'save_schema_meta')) {
return;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (!current_user_can('edit_post', $post_id)) {
return;
}
// Save fields
if (isset($_POST['rating'])) {
update_post_meta($post_id, 'rating', sanitize_text_field($_POST['rating']));
}
if (isset($_POST['review_count'])) {
update_post_meta($post_id, 'review_count', absint($_POST['review_count']));
}
if (isset($_POST['price'])) {
update_post_meta($post_id, 'price', sanitize_text_field($_POST['price']));
}
}
add_action('save_post', 'save_schema_meta');
Validation and Testing
Ensure schema correctness.
Google Rich Results Test
Test URL: https://search.google.com/test/rich-results
Process:
- Enter your URL or paste schema code
- Click “Test URL” or “Test Code”
- Review validation results
- Fix any errors or warnings
Schema Markup Validator
Official Tool: https://validator.schema.org/
Features:
- Validates JSON-LD syntax
- Checks property compatibility
- Identifies schema errors
- Suggests improvements
Common Validation Errors
Missing Required Properties:
// ERROR - missing required 'name'
$schema = [
'@type' => 'Person'
// Missing 'name' property
];
// CORRECT
$schema = [
'@type' => 'Person',
'name' => 'John Doe' // Required
];
Incorrect Data Types:
// ERROR - price should be number
'price' => '$99'
// CORRECT
'price' => '99.00',
'priceCurrency' => 'USD'
Invalid URLs:
// ERROR - not a valid URL
'url' => 'example.com'
// CORRECT
'url' => 'https://example.com'
Best Practices
Professional schema implementation.
Use Constants for @context
const SCHEMA_CONTEXT = 'https://schema.org';
$schema = [
'@context' => SCHEMA_CONTEXT,
'@type' => 'Article'
];
Sanitize All Data
$schema = [
'@type' => 'Article',
'headline' => esc_html(get_the_title()),
'description' => esc_html(get_the_excerpt()),
'url' => esc_url(get_permalink())
];
Check for Empty Values
$rating = get_post_meta($post_id, 'rating', true);
if (!empty($rating)) {
$schema['aggregateRating'] = [
'@type' => 'AggregateRating',
'ratingValue' => $rating
];
}
Use ISO 8601 for Dates
// Correct format
'datePublished' => get_the_date('c', $post_id), // 2025-01-15T10:30:00+00:00
// Wrong
'datePublished' => get_the_date('F j, Y') // January 15, 2025
Document Custom Schemas
/**
* Generate SoftwareApplication schema for plugin posts.
*
* @param int $post_id Post ID.
* @return array Schema markup array.
*/
function custom_software_schema($post_id) {
// Implementation
}
Conclusion
Creating custom schema types in WordPress extends structured data beyond standard implementations. By understanding Schema.org vocabulary, JSON-LD format, and WordPress development practices, you can implement any schema type for your specific needs.
Key Implementation Steps:
- Choose appropriate Schema.org type
- Define required and recommended properties
- Create PHP function to build schema array
- Output JSON-LD in wp_head
- Create admin interface for data input
- Validate with Google Rich Results Test
- Test with Schema.org validator
- Monitor Search Console for enhancements
Development Checklist:
- ✓ Use proper @context and @type
- ✓ Include all required properties
- ✓ Sanitize and escape data
- ✓ Use correct data types
- ✓ Validate before deployment
- ✓ Check for empty values
- ✓ Use ISO 8601 for dates
- ✓ Test in multiple validators
Start by extending Nexus Pro’s existing schemas with custom properties, then progress to creating completely custom schema types for specialized content. Always validate implementations before deployment to ensure Google compatibility.
Related Articles:

