# Spatie Schema.org
spatie/schema-org is a PHP library that provides a fluent builder interface for creating Schema.org structured data in JSON-LD format. The library auto-generates classes from the official Schema.org vocabulary, offering type-safe construction of all Schema.org types including LocalBusiness, Product, Article, Event, Person, Organization, and over 800 other types. Each type can be instantiated using either a factory method or direct construction, configured through chainable method calls, and rendered as JSON-LD script tags for embedding in HTML pages.
The package supports advanced features like conditional property setting, array access for properties, nested types, multi-typed entities (combining multiple types into one), and graph structures (organizing multiple related schema objects). It handles automatic serialization of complex data types including DateTime objects, maintains proper JSON-LD context formatting, and provides utilities for hiding specific nodes in graphs. The library is particularly useful for improving SEO by adding rich snippets to websites, enabling search engines to better understand page content.
## Schema Factory
Create Schema.org type instances using factory methods.
```php
use Spatie\SchemaOrg\Schema;
// Create a LocalBusiness with properties
$business = Schema::localBusiness()
->name('Spatie')
->email('info@spatie.be')
->url('https://spatie.be')
->telephone('+32-3-292-56-79')
->priceRange('$$$')
->address(Schema::postalAddress()
->streetAddress('Kruikstraat 22')
->addressLocality('Antwerp')
->postalCode('2018')
->addressCountry('BE')
);
echo $business->toScript();
```
## Product with Offers
Create product schemas with nested offer information.
```php
use Spatie\SchemaOrg\Schema;
$product = Schema::product()
->name('Executive Anvil')
->image('https://example.com/photos/anvil.jpg')
->description('Sleeker than ACME\'s Classic Anvil')
->sku('0446310786')
->brand(Schema::brand()->name('ACME'))
->offers(Schema::offer()
->url('https://example.com/anvil')
->priceCurrency('USD')
->price('119.99')
->priceValidUntil('2025-12-31')
->itemCondition('https://schema.org/NewCondition')
->availability('https://schema.org/InStock')
)
->aggregateRating(Schema::aggregateRating()
->ratingValue('4.4')
->reviewCount('89')
);
echo $product->toScript();
// Outputs:
```
## Article Schema
Build article schemas for blog posts and news content.
```php
use Spatie\SchemaOrg\Schema;
$article = Schema::article()
->headline('My Awesome Article')
->datePublished(new DateTime('2025-01-15'))
->dateModified(new DateTime('2025-01-16'))
->author(Schema::person()
->name('John Doe')
->url('https://example.com/authors/john')
)
->publisher(Schema::organization()
->name('Example Publisher')
->logo(Schema::imageObject()
->url('https://example.com/logo.png')
->width(600)
->height(60)
)
)
->image('https://example.com/article-image.jpg')
->articleBody('Full text of the article...');
echo json_encode($article, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
```
## Array Access and Property Methods
Access and manipulate schema properties using array notation or methods.
```php
use Spatie\SchemaOrg\Schema;
$business = Schema::localBusiness();
// Set properties using methods
$business->name('Spatie');
// Set properties using array access
$business['email'] = 'info@spatie.be';
// Check if property exists
if (isset($business['name'])) {
echo $business['name']; // 'Spatie'
}
// Get property with default
echo $business->getProperty('phone', 'Not provided');
// Get all properties
$properties = $business->getProperties();
// Unset a property
unset($business['email']);
// Add multiple properties at once
$business->addProperties([
'telephone' => '+32-3-292-56-79',
'address' => 'Kruikstraat 22, Antwerp'
]);
```
## Conditional Property Setting
Conditionally set properties without breaking method chains.
```php
use Spatie\SchemaOrg\Schema;
use Spatie\SchemaOrg\LocalBusiness;
$businessData = [
'name' => 'Spatie',
'email' => 'info@spatie.be',
// 'phone' is optional
];
$business = Schema::localBusiness()
->name($businessData['name'])
->email($businessData['email'])
->if(isset($businessData['phone']), function (LocalBusiness $schema) use ($businessData) {
$schema->telephone($businessData['phone']);
})
->if(isset($businessData['priceRange']), function (LocalBusiness $schema) use ($businessData) {
$schema->priceRange($businessData['priceRange']);
});
echo $business->toScript();
```
## Event Schema
Create event schemas with dates and locations.
```php
use Spatie\SchemaOrg\Schema;
$event = Schema::event()
->name('Laravel Conference 2025')
->startDate(new DateTime('2025-03-15T09:00:00'))
->endDate(new DateTime('2025-03-17T17:00:00'))
->eventStatus('https://schema.org/EventScheduled')
->eventAttendanceMode('https://schema.org/OfflineEventAttendanceMode')
->location(Schema::place()
->name('Conference Center')
->address(Schema::postalAddress()
->streetAddress('123 Main St')
->addressLocality('Amsterdam')
->postalCode('1000')
->addressCountry('NL')
)
)
->offers(Schema::offer()
->url('https://example.com/tickets')
->price('299.00')
->priceCurrency('EUR')
->availability('https://schema.org/InStock')
->validFrom(new DateTime('2025-01-01'))
)
->performer(Schema::person()
->name('Taylor Otwell')
);
echo $event->toScript();
```
## Breadcrumb List
Create breadcrumb navigation schemas for SEO.
```php
use Spatie\SchemaOrg\Schema;
$breadcrumbs = Schema::breadcrumbList()
->itemListElement([
Schema::listItem()
->position(1)
->name('Home')
->item('https://example.com'),
Schema::listItem()
->position(2)
->name('Products')
->item('https://example.com/products'),
Schema::listItem()
->position(3)
->name('Anvils')
->item('https://example.com/products/anvils'),
Schema::listItem()
->position(4)
->name('Executive Anvil')
]);
echo $breadcrumbs->toScript();
```
## Enumeration Types
Use enumeration constants for type-safe property values.
```php
use Spatie\SchemaOrg\Schema;
use Spatie\SchemaOrg\BookFormatType;
use Spatie\SchemaOrg\ItemAvailability;
$book = Schema::book()
->name('Laravel: Up & Running')
->bookFormat(BookFormatType::EBook)
->isbn('978-1491936122')
->author(Schema::person()->name('Matt Stauffer'))
->offers(Schema::offer()
->price('34.99')
->priceCurrency('USD')
->availability(ItemAvailability::InStock)
);
echo $book->toScript();
```
## Multi-Typed Entity
Combine multiple Schema.org types into a single entity.
```php
use Spatie\SchemaOrg\Schema;
use Spatie\SchemaOrg\MultiTypedEntity;
use Spatie\SchemaOrg\Product;
$mte = new MultiTypedEntity();
// Add HotelRoom type
$mte->hotelRoom()
->name('The Presidential Suite')
->occupancy(Schema::quantitativeValue()->value(4));
// Add Product type with offer
$mte->product()
->offers(Schema::offer()
->name('One Night Stay')
->price(100000.00)
->priceCurrency('USD')
);
// Add additional Product properties via closure
$mte->product(function (Product $product) {
$product->aggregateRating(
Schema::aggregateRating()
->bestRating(5)
->worstRating(4)
->ratingValue(4.7)
);
});
echo json_encode($mte, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
// Outputs: {"@context":"https://schema.org","@type":["HotelRoom","Product"],...}
```
## Graph with Multiple Entities
Create a graph containing multiple related schema objects.
```php
use Spatie\SchemaOrg\Graph;
use Spatie\SchemaOrg\Schema;
use Spatie\SchemaOrg\Organization;
$graph = new Graph();
// Create a product and reference an organization
$graph->product()
->name('Executive Anvil')
->brand($graph->organization())
->manufacturer($graph->organization());
// Fill out the organization details (referenced above)
$graph->organization()
->name('ACME Corporation')
->url('https://acme.example.com')
->logo('https://acme.example.com/logo.png');
// Add another product
$graph->product('anvil-lite')
->name('Anvil Lite')
->brand($graph->organization());
echo $graph->toScript();
// Outputs:
```
## Graph with Node Identifiers
Manage multiple nodes of the same type using identifiers.
```php
use Spatie\SchemaOrg\Graph;
use Spatie\SchemaOrg\Schema;
use Spatie\SchemaOrg\Person;
$graph = new Graph();
// Add team members with identifiers
$graph->person('freek')
->givenName('Freek')
->familyName('Van der Herten')
->alternateName('freekmurze')
->jobTitle('Developer');
$graph->person('sebastian', function(Person $person, Graph $graph): void {
$person
->givenName('Sebastian')
->familyName('De Deyne')
->alternateName('sebastiandedeyne')
->jobTitle('Developer');
});
$graph->person('tom')
->givenName('Tom')
->familyName('Witkowski')
->alternateName('gummibeer')
->jobTitle('Developer');
// Add organization with team members
$graph->organization()
->name('Spatie')
->employee([
$graph->person('freek'),
$graph->person('sebastian'),
$graph->person('tom')
]);
// Optionally hide specific nodes from output
$graph->hide(Person::class, 'tom');
echo json_encode($graph, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
```
## Custom Properties and Identifiers
Set custom properties and identifiers on schema types.
```php
use Spatie\SchemaOrg\Schema;
$organization = Schema::organization()
->name('Spatie')
->setProperty('identifier', 'https://spatie.be/#organization')
->setProperty('customProperty', 'custom value');
// Identifiers are converted to @id in JSON-LD
echo $organization->toArray()['@id']; // 'https://spatie.be/#organization'
// Get properties
$name = $organization->getProperty('name');
$all = $organization->getProperties();
```
## Recipe Schema
Create detailed recipe schemas for food content.
```php
use Spatie\SchemaOrg\Schema;
$recipe = Schema::recipe()
->name('Chocolate Chip Cookies')
->image('https://example.com/cookies.jpg')
->author(Schema::person()->name('Chef John'))
->datePublished(new DateTime('2025-01-10'))
->description('The best chocolate chip cookies recipe')
->prepTime('PT15M')
->cookTime('PT10M')
->totalTime('PT25M')
->recipeYield('24 cookies')
->recipeCategory('Dessert')
->recipeCuisine('American')
->keywords(['cookies', 'chocolate', 'baking'])
->recipeIngredient([
'2 cups all-purpose flour',
'1 cup butter, softened',
'1 cup white sugar',
'2 eggs',
'2 cups chocolate chips'
])
->recipeInstructions([
Schema::howToStep()->text('Preheat oven to 350°F (175°C)'),
Schema::howToStep()->text('Mix butter and sugar until fluffy'),
Schema::howToStep()->text('Beat in eggs one at a time'),
Schema::howToStep()->text('Stir in flour and chocolate chips'),
Schema::howToStep()->text('Drop spoonfuls onto baking sheet'),
Schema::howToStep()->text('Bake for 10 minutes until golden')
])
->nutrition(Schema::nutritionInformation()
->calories('180 calories')
->fatContent('9g')
->sugarContent('12g')
);
echo $recipe->toScript();
```
## FAQ Page Schema
Create FAQ schemas for question-and-answer content.
```php
use Spatie\SchemaOrg\Schema;
$faq = Schema::fAQPage()
->mainEntity([
Schema::question()
->name('How do I install the package?')
->acceptedAnswer(Schema::answer()
->text('Run: composer require spatie/schema-org')
),
Schema::question()
->name('What PHP versions are supported?')
->acceptedAnswer(Schema::answer()
->text('PHP 8.0 and higher is required.')
),
Schema::question()
->name('Is it free to use?')
->acceptedAnswer(Schema::answer()
->text('Yes, the package is open source under the MIT license.')
)
]);
echo $faq->toScript();
```
## Video Object Schema
Create video schemas for video content SEO.
```php
use Spatie\SchemaOrg\Schema;
$video = Schema::videoObject()
->name('How to Build Schema.org Structured Data')
->description('A tutorial on implementing Schema.org in PHP')
->thumbnailUrl('https://example.com/video-thumbnail.jpg')
->uploadDate(new DateTime('2025-01-15'))
->duration('PT15M33S')
->contentUrl('https://example.com/video.mp4')
->embedUrl('https://example.com/embed/video')
->interactionStatistic(Schema::interactionCounter()
->interactionType('https://schema.org/WatchAction')
->userInteractionCount(5420)
);
echo $video->toScript();
```
## Array Serialization
Convert schemas to arrays for custom processing.
```php
use Spatie\SchemaOrg\Schema;
$person = Schema::person()
->name('Jane Smith')
->jobTitle('Software Engineer')
->email('jane@example.com')
->worksFor(Schema::organization()
->name('Tech Corp')
);
// Get as array
$array = $person->toArray();
/*
[
'@context' => 'https://schema.org',
'@type' => 'Person',
'name' => 'Jane Smith',
'jobTitle' => 'Software Engineer',
'email' => 'jane@example.com',
'worksFor' => [
'@type' => 'Organization',
'name' => 'Tech Corp'
]
]
*/
// Get as JSON string
$json = json_encode($person, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
```
## CSP Nonce Support
Add nonce attributes for Content Security Policy compliance.
```php
use Spatie\SchemaOrg\Schema;
$nonce = 'random-nonce-value-123';
$business = Schema::localBusiness()
->name('Spatie')
->setNonce($nonce);
echo $business->toScript();
// Outputs:
// Also works with Graph
$graph = new Graph();
$graph->setNonce($nonce);
$graph->organization()->name('Spatie');
echo $graph->toScript();
```
## Summary
The spatie/schema-org library simplifies the creation of Schema.org structured data for PHP applications, offering comprehensive coverage of the entire Schema.org vocabulary through auto-generated, type-safe classes. Primary use cases include enhancing e-commerce sites with Product schemas, improving local business SEO with LocalBusiness schemas, optimizing content discoverability with Article and BlogPosting schemas, adding rich event information with Event schemas, and creating structured FAQ and HowTo content. The library handles complex nested structures, supports conditional property setting for dynamic content, and provides proper JSON-LD serialization with context management.
Integration patterns include embedding schemas directly in HTML views using the toScript() method, building complex multi-entity relationships with Graph objects, combining multiple types for hybrid entities using MultiTypedEntity, and exporting schemas as arrays or JSON for API responses or custom processing. The fluent API design makes it easy to construct schemas programmatically from database records or CMS content, while built-in support for DateTime serialization, enumeration types, and CSP nonces ensures production-ready output. The library is particularly valuable for Laravel applications but works with any PHP framework or standalone application requiring SEO-optimized structured data.