Schema-org is a ColdBox module for building structured JSON-LD data using Schema.org in a flexible, fluent, and framework-friendly way.
Schema.org is a collaborative vocabulary standard (initiated by Google, Microsoft, Yahoo, and Yandex) that helps search engines understand webpage content.
JSON-LD (JavaScript Object Notation for Linked Data) is a data format that allows you to express structured data in a way that is easy for both humans and machines to read. It is ideal for embedding structured information in web pages.
Using Schema.org structured data can improve your website's visibility in search results by allowing search engines to understand your content more effectively. It also enables rich snippets, which can display additional information like ratings, reviews, and product details directly in search results. Bottom line: it's an essential part of modern SEO and web development.
- ColdBox: 7.0+
- CFML Engines:
- Adobe ColdFusion 2021+
- Lucee 5.3+
- BoxLang 1.0.0+
- β Easy, fluent API for creating schema types
- π Supports multiple schemas per page
- π Automatic
@contextand@graphwrapping - π‘ Outputs valid JSON-LD
Install via CommandBox:
box install schema-orgWoah, what? Nothing to configure? That's awesome!
- Inject the
SchemaBuilderinto your handler/layout/component or alternatively, instantiate an instance ofSchemaBuildervia wirebox. Important:SchemaBuilderis a transient, so make sure to use theprovider:method when using property inection.
// automatic injetcion
property name="sb" inject="provider:SchemaBuilder@schema-org";
// or manual instantiation
var sb = getInstance( "SchemaBuilder@schema-org" );- Build your schema objects. The method names match the Schema.org object types. You can use either a closure or a struct to define properties for each schema type.
// In your handler method
prc.schema = sb
// Organization Schema
.organization( function( o ) {
o.id( "https://startrek.com/##organization" ) // must be unique
.name( "United Federation of Planets" )
.url( "https://startrek.com/" )
// ... rest of schema properties
} )
// Webpage Schema
.webpage( function( o ) {
o.id( https://startrek.com/##webpage" ) // must be unique
.name( "Starfleet Command" )
.description( "Official website of Starfleet Command" )
.url( "https://startrek.com/" )
// ... rest of schema properties
} )- Render the JSON-LD in your view:
<cfoutput>
#prc.schema.render()#
</cfoutput>-
get(): Returns a struct representation of the full schema graph. -
render(): Returns the serialized JSON-LD string wrapped in<script type="application/ld+json">tags for easy embedding in HTML. -
toArray(): Returns an array of all schema objects created. -
toJsonLd(): Returns serialized JSON-LD schema graph (without the script tags). -
[type]()methods: Create specific schema types likeorganization(),webpage(),product(), etc. The method accepts a closure or a struct to define properties (see below).
Each supported type is accessible via method call on the builder. Examples include:
organization()webpage()product()localBusiness()etc...
All core Schema.org types are supported. If you discover a missing one, please open an issue.
Each schema object type has methods to set properties and instantiate new objects. Common methods include:
-
id(value): Sets the unique identifier for the schema object. Note: The actual property name is@id, but you can useid()orsetId()for convenience. -
[property](value): Sets a property for the schema object. The method name corresponds to the Schema.org property name (e.g.,name(value),url(value),description(value), etc.). -
new(type, closure or struct): Creates a new nested schema object of the specified type. Thetypeis the Schema.org type name (e.g., "PostalAddress"), and theclosureis a function or a struct to define its properties.
You can define properties using a closure or a struct. The closure approach allows for fluent chaining, while the struct approach is more explicit. The methods available for each type are based on the properties available for that particular type.
The below examples assume sb is an instance of SchemaBuilder. The closure receives a single argument, which is the schema object being built. You can chain methods to set properties.
sb.organization( function( o ) {
o.id( "https://example.com/##organization" )
.name( "Example Organization" )
.url( "https://example.com/" )
// ... other properties
} )sb.organization( {
id: "https://example.com/##organization",
name: "Example Organization",
url: "https://example.com/"
// ... other properties
} )You can nest schema objects within each other. You can quickly create a new instance of a schema type by calling new() on the closure argument. For example, if you have a localBusiness that has an address, you can define it like this:
sb.localBusiness( function( lb ) {
lb.id( "https://example.com/##localBusiness" )
.name( "Example Local Business" )
// Address needs a nested schema object
.address(
// Create a new PostalAddress instance by calling new() on the schema object
lb.new( "PostalAddress", function( a ) {
a.streetAddress( "123 Example St" )
.addressLocality( "Example City" )
.addressRegion( "EX" )
.postalCode( "12345" )
.addressCountry( "US" );
} )
);
} )Here's a complete example of how you might use the SchemaBuilder in a ColdBox app. You can also check out the test-harness folder in this repo for the more fun.
// /layouts/Main.cfc
<script type="application/ld+json">
#runEvent(
event = "schemas.index",
eventArguments = {
<!--- Pass along important event arguments for schema generation --->
<!--- Tip: If you use event caching, the cache key is determined by these arguments, so make them unique per page/schema --->
eventName = event.getCurrentEvent(), <!--- example: "posts.show" --->
routedUrl = event.getCurrentRoutedUrl() <!--- example: "/posts/why-captain-picard-is-the-goat" --->
}
)#
</script>Note that when building the webpage schema, we recommend using variables like prc.canonicalUrl, prc.pageTitle, and prc.pageDescription to dynamically set the schema properties based on the current page context. This allows for flexible schema generation across different pages without hardcoding values. All your pages should have these properties which is a best-practice for any modern website where SEO is a priority.
// /handlers/Schemas.cfc
// Inject the SchemaBuilder into your handler
property name="sb" inject="provider:SchemaBuilder@schema-org";
function index( event, rc, prc, eventName, routedUrl ) {
// Build the schema objects
prc.schema = sb
// add the organization schema based on Coldbox config settings
.organization( getSetting( "schema" ).organization )
// add the webpage schema based on Coldbox config settings
.website( getSetting( "schema" ).website )
// Dynamically build the webpage schema based on the current event and routed URL
.webpage( function( o ) {
o.id( prc.canonicalUrl & "$##webpage" )
.name( prc.pageTitle )
.description( prc.pageDescription )
.url( prc.canonicalUrl );
} );
// Return the schema as JSON-LD
return prc.schema.toJsonLd();
}- Use the canonical URL for url, not the current browser path.
- Include an
Organizationon every main page (for branding/trust). - Use stable
@idvalues (especially for shared entities like authors or logos). - Use
WebPageon all routable pages.
If you're using Adobe ColdFusion (ACF), you may encounter issues when setting a property that is a reserved word in ACF, such as abstract, which is found in the Schema.org CreativeWork type. To work around this limitation, you can populate the property using a struct or prepend an underscore _ to the method call, like this:
// MyHandler.cfc
// Closure workaround 1
prc.schema = schemaBuilder.creativeWork( function( cw ) {
cw.id( "https://example.com/##creativeWork" )
.name( "Example Creative Work" )
._abstract( "This is an example abstract." );
} );
// Closure workaround 2
prc.schema = schemaBuilder.creativeWork( function( cw ) {
cw.id( "https://example.com/##creativeWork" )
.name( "Example Creative Work" )
.set_abstract( "This is an example abstract." );
} );
// Struct workaround
prc.schema = schemaBuilder.creativeWork( {
id: "https://example.com/##creativeWork",
name: "Example Creative Work",
abstract: "This is an example abstract." // no need for underscore
} );- Vote for Adobe to fix this issue in ACF 2023: CF-4226929.
- Vote for Adobe to fix this issue in ACF 2021: CF-4226928.
Ideas? Bugs? Pull requests welcome! Please include tests when submitting PRs.
This module was forged by Angry Sam Productions, a California-based web development company. We love ColdBox and believe structured data is critical for modern SEO.
To run the tests, simply run the following command from the root of the project in Commandbox:
start [email protected] (or whichever server JSON you want to use)
server open (to open the server in your browser)
navigate to /tests/runner.cfm in your browser.
This module is provided "as is" without warranty of any kind. Use at your own risk. The author is not responsible for any issues that may arise from using this module in your applications.