Annotated JSON-LD Structured Data Examples
Tags: seo, jsonld, structureddata
Published: 2020-02-25 21:19:58

---
title: Annotated JSON-LD Structured Data Examples
published: true
date: 2019-05-29 12:00:00 UTC
tags: seo,json-ld,structured-data
canonical_url: https://nystudio107.com/blog/annotated-json-ld-structured-data-examples
---

# Annotated JSON-LD Structured Data Examples

### Want to see a some real-world exam­ples of JSON-LD Struc­tured Data ​“in the wild”? This arti­cle has that, plus annotation.

Andrew Welch / [nystudio107](https://nystudio107.com)

![Json ld structured data in the wild](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x675_crop_center-center_82_line/json-ld-structured-data-in-the-wild.jpg)

[Struc­tured data](https://developers.google.com/search/docs/guides/intro-structured-data) allows you to tell search engines con­tex­tu­al infor­ma­tion about what is on your web pages, rather than leav­ing it up to them to guess. It uses the stan­dard­ized vocab­u­lary from [schema​.org](https://schema.org/docs/full.html) expressed via [JSON-LD](https://json-ld.org/)to con­vey the con­text and con­nec­tions between data.

Many arti­cles talk about struc­tured data; this arti­cle is going to focus on real-world exam­ples of using struc­tured data ​“in the wild”. It uses the [SEO­mat­ic](https://nystudio107.com/plugins/seomatic) plu­g­in for [Craft CMS](https://craftcms.com/) in the exam­ples, but this arti­cle pro­vides tool­ing-agnos­tic anno­tat­ed JSON-LD struc­tured data exam­ples that any­one can use.

I’m not going to try to sell you on the ben­e­fits of using struc­tured data in this arti­cle. I’m going to assume that you’re already on board, and want to see some real-world, anno­tat­ed examples.

For more infor­ma­tion on JSON-LD struc­tured data in gen­er­al, check out the [JSON-LD, Struc­tured Data and Erot­i­ca](https://dev.to/gaijinity/json-ld-structured-data-and-erotica-51ph-temp-slug-1704763) article.

## A Gen­er­al Approach to Struc­tured Data

While there are sev­er­al for­mats for struc­tured data, Google, Apple, and oth­er major play­ers in the indus­try have made it clear that [JSON-LD](https://json-ld.org/) is the way for­ward. JSON-LD is the same JSON we’re used to from JavaScript, with the LD stand­ing for ​“Linked Data”.

[Schema​.org](https://schema.org/docs/full.html) defines a large vocab­u­lary of struc­tured data schemas to choose from; how do we decide what to imple­ment? My approach is twofold:

- Define schema types as explic­it­ly as pos­si­ble using a ​“best fit” approach
- Use schema types that [Google has pub­licly supported](https://developers.google.com/search/docs/data-types/article)

After all, we’re putting struc­tured data onto our web­site so that search engines can under­stand it, but we also want to be prag­mat­ic about it, and focus our ener­gy on the schema types that we know Google will consume.

<aside>
                                Google only con­cerns itself with a sub­set of schema​.org types
                            </aside>

Check out the schema types that [Google has pub­licly sup­port­ed](https://developers.google.com/search/docs/data-types/article) and also [explore the Google Search Gallery](https://developers.google.com/search/docs/guides/search-gallery) for an inter­ac­tive look at how schema types can affect not just the knowl­edge graph, but also the Search Engine Results Page (SERP).

## Node Iden­ti­fiers and mainEntityOfPage

Before we get into the spe­cif­ic exam­ples, there are a cou­ple of gen­er­al con­cepts that I want to cover.

The first is that when rep­re­sent­ing JSON-LD, I make heavy use of [Node Iden­ti­fiers](https://www.w3.org/TR/2014/REC-json-ld-20140116/#node-identifiers) (the <kbd>"@id"</kbd> prop­er­ties) to ref­er­ence spe­cif­ic schemas with­out repeat­ing data need­less­ly. We can have an <kbd>Organization</kbd> schema and then just ref­er­ence it from mul­ti­ple places, rather than repli­cat­ing the entire schema data each time.

Here’s a trun­cat­ed example:

```

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@id": "our-organization",
    "@type": "Organization"
}

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@type": "Article",
    "publisher": {
        "@id": "our-organization"
    }
}
</script>

```

In this case we used <kbd>our-organization</kbd> as a Node Iden­ti­fi­er, but often just as a con­ven­tion we can use URLs or URLs with hash­es so that it feels name spaced. It real­ly does­n’t mat­ter, as long as it is local­ly unique on your webpage.

Think of [Node Iden­ti­fiers](https://www.w3.org/TR/2014/REC-json-ld-20140116/#node-identifiers) as a way to define a label or alias to anoth­er node in your local schema graph. You assign a local­ly unique <kbd>"@id"</kbd> to a JSON-LD schema, and then you can refer to that <kbd>"@id"</kbd> from oth­er JSON-LD schemas.

More on Node Iden­ti­fiers can be found in the [What is the use of @id in json-ld syn­tax?](https://webmasters.stackexchange.com/questions/98569/what-is-the-use-of-id-in-json-ld-syntax) and [Schema​.org JSON-LD ref­er­ence](https://stackoverflow.com/questions/34761970/schema-org-json-ld-reference) articles.

The [mainEn­ti­ty­Of­Page](https://schema.org/docs/datamodel.html#mainEntityBackground) prop­er­ty also deserves spe­cial mention:

<aside>
                                Indi­cates a page (or oth­er Cre­ative­Work) for which this thing is the main enti­ty being described
                            </aside>

So if a web page is an arti­cle like the one you’re read­ing right now, it’ll have [Arti­cle](https://schema.org/Article) JSON-LD with the <kbd>mainEntityOfPage</kbd> prop­er­ty’s val­ue being the URL to the arti­cle itself:

```

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@type": "Article",
    "mainEntityOfPage": "https://nystudio107.com/blog/implementing-custom-json-ld-structured-data"
}
</script>

```

It’s say­ing ​“Hey, I’m the main sub­ject of the web page at this URL!”

Now let’s dive into look­ing at some spe­cif­ic examples.

## nys​tu​dio107​.com Arti­cle Page

Let’s start by look­ing at the struc­tured data for the page you’re read­ing right now. Just by choos­ing a few set­tings of how to map data, here’s what you’ll get by default from [SEO­mat­ic](https://nystudio107.com/plugins/seomatic):

```

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@type": "Article",
    "author": {
        "@id": "https://nystudio107.com/#identity"
    },
    "copyrightHolder": {
        "@id": "https://nystudio107.com/#identity"
    },
    "copyrightYear": "2019",
    "creator": {
        "@id": "https://nystudio107.com/#creator"
    },
    "dateModified": "2019-05-28T17:33:57-04:00",
    "datePublished": "2019-05-27T08:45:00-04:00",
    "description": "Want to see a some real-world examples of JSON-LD structured data \"in the wild\"? This article has that, plus annotation.",
    "headline": "Implementing Custom JSON-LD Structured Data",
    "image": {
        "@type": "ImageObject",
        "url": "https://nystudio107.com/img/blog/_1200x630_crop_center-center_82_none/json-ld-structured-data-in-the-wild.jpg"
    },
    "inLanguage": "en",
    "mainEntityOfPage": "https://nystudio107.com/blog/implementing-custom-json-ld-structured-data",
    "name": "Implementing Custom JSON-LD Structured Data",
    "publisher": {
        "@id": "https://nystudio107.com/#creator"
    },
    "url": "https://nystudio107.com/blog/implementing-custom-json-ld-structured-data"
}
</script>

```

Most things in this [Arti­cle](https://schema.org/Article) schema are pret­ty straight­for­ward; but why did we choose [Arti­cle](https://schema.org/Article) to begin with? Why not [Blog­Post­ing](https://schema.org/BlogPosting)?

The answer is sim­ple. Google [lists Arti­cle](https://developers.google.com/search/docs/data-types/article) as an explic­it­ly sup­port­ed type. Addi­tion­al­ly, if we have [Google AMP](https://dev.to/gaijinity/google-amp-should-you-care-3din-temp-slug-933989) ver­sions of our pages (and we do for [this arti­cle](https://nystudio107.com/blog/annotated-json-ld-structured-data-examples/amp)), this <kbd>Article</kbd> JSON-LD is required for your arti­cle to appear in the carousel of AMP stories.

Note that we are using [Node Iden­ti­fiers](https://www.w3.org/TR/2014/REC-json-ld-20140116/#node-identifiers) with <kbd>"@id"</kbd> in sev­er­al places to refer to oth­er JSON-LD struc­tured data nodes rather than repeat­ing the content.

<aside>
                                By doing this, we are Link­ing Data nodes togeth­er… the <span>LD</span> part of <span>JSON-LD</span>.
                            </aside>

The oth­er key thing to note is the [mainEn­ti­ty­Of­Page](https://schema.org/docs/datamodel.html#mainEntityBackground) prop­er­ty, which is iden­ti­fy­ing the fact that this [Arti­cle](https://schema.org/Article) JSON-LD struc­tured data is the pri­ma­ry sub­ject of the web page at the list­ed URL.

This helps tell Google what you con­sid­er to be the impor­tant part of or thing on the page, rather than hav­ing it try to guess it.

This [Orga­ni­za­tion](https://schema.org/Organization) schema is for the enti­ty that owns the web­site (note the <kbd>"@id": "https://nystudio107.com/#identity"</kbd> Node Identifier):

```

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@id": "https://nystudio107.com/#identity",
    "@type": "Organization",
    "address": {
        "@type": "PostalAddress",
        "addressCountry": "US",
        "addressLocality": "Webster",
        "addressRegion": "NY",
        "postalCode": "14580"
    },
    "alternateName": "nys",
    "description": "We do technology-based consulting, branding, design, and development. Making the web better one site at a time, with a focus on performance, usability & SEO",
    "email": "info@nystudio107.com",
    "founder": "Andrew Welch, Polly Welch",
    "foundingDate": "2013-05-02",
    "foundingLocation": "Webster, NY",
    "image": {
        "@type": "ImageObject",
        "height": "2048",
        "url": "https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/site/nys_logo_seo.png",
        "width": "2048"
    },
    "logo": {
        "@type": "ImageObject",
        "height": "60",
        "url": "https://nystudio107.com/img/site/_600x60_fit_center-center_82_none/nys_logo_seo.png",
        "width": "600"
    },
    "name": "nystudio107",
    "sameAs": [
        "https://twitter.com/nystudio107",
        "https://www.facebook.com/newyorkstudio107",
        "https://plus.google.com/+nystudio107com",
        "https://www.youtube.com/channel/UCOZTZHQdC-unTERO7LRS6FA",
        "https://github.com/nystudio107"
    ],
    "url": "https://nystudio107.com/"
}
</script>

```

It exists pri­mar­i­ly so that oth­er JSON-LD struc­tured data such as the <kbd>Article</kbd> can link to it and say ​“this is the author” and ​“this is the copy­right hold­er” and so on and so forth.

You could take this much fur­ther and add a list of peo­ple who work at the orga­ni­za­tion via the <kbd>employee</kbd> prop­er­ty, or list peo­ple who used to work at the orga­ni­za­tion via the <kbd>alumni</kbd> property.

In this way, you build up the con­text and rela­tion­ship between things to help Google under­stand the con­nec­tions between them.

This [Orga­ni­za­tion](https://schema.org/Organization) schema is for the enti­ty that cre­at­ed the web­site (note the <kbd>"@id": "https://nystudio107.com/#creator"</kbd> Node Identifier):

```

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@id": "https://nystudio107.com/#creator",
    "@type": "Organization",
    "address": {
        "@type": "PostalAddress",
        "addressCountry": "US",
        "addressLocality": "Webster",
        "addressRegion": "NY",
        "postalCode": "14580"
    },
    "alternateName": "nys",
    "description": "We do technology-based consulting, branding, design, and development. Making the web better one site at a time, with a focus on performance, usability & SEO",
    "email": "info@nystudio107.com",
    "founder": "Andrew Welch, Polly Welch",
    "foundingDate": "2013-05-02",
    "foundingLocation": "Webster, NY",
    "image": {
        "@type": "ImageObject",
        "height": "1042",
        "url": "https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/site/nys_logo_seo.png",
        "width": "1042"
    },
    "logo": {
        "@type": "ImageObject",
        "height": "60",
        "url": "https://nystudio107.com/img/site/_600x60_fit_center-center_82_none/nys_logo_seo.png",
        "width": "600"
    },
    "name": "nystudio107",
    "url": "https://nystudio107.com/"
}
</script>

```

It exists pri­mar­i­ly so that oth­er JSON-LD struc­tured data such as the <kbd>Article</kbd> can link to it and say ​“this is the cre­ator” and so on and so forth.

Just as with the <kbd>identity</kbd> [Orga­ni­za­tion](https://schema.org/Organization), you can take this much fur­ther to add addi­tion­al infor­ma­tion about <kbd>employee</kbd>s, <kbd>alumni</kbd>, etc.

Final­ly we have the auto­mat­i­cal­ly gen­er­at­ed [Bread­crum­b­List](https://schema.org/BreadcrumbList) schema that indi­cates the page’s posi­tion in the site hierarchy:

```

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@type": "BreadcrumbList",
    "description": "Breadcrumbs list",
    "itemListElement": [
        {
            "@type": "ListItem",
            "item": {
                "@id": "https://nystudio107.com/",
                "name": "Homepage"
            },
            "position": 1
        },
        {
            "@type": "ListItem",
            "item": {
                "@id": "https://nystudio107.com/blog",
                "name": "Blog Index"
            },
            "position": 2
        },
        {
            "@type": "ListItem",
            "item": {
                "@id": "hthttps://nystudio107.comt/blog/tips-for-using-seomatic-effectively",
                "name": "Advanced SEOmatic Tips"
            },
            "position": 3
        }
    ],
    "name": "Breadcrumbs"
}
</script>

```

This is a [sup­port­ed Google type](https://developers.google.com/search/docs/data-types/breadcrumb) that helps it under­stand the struc­ture of your web­site, and also can show up as actu­al bread­crumb links on your Search Engine Results Page (SERP).

So this is a nice start­ing point, but we can do a bit bet­ter. Let’s use the [SEO­mat­ic](https://nystudio107.com/plugins/seomatic) Twig API to add a lit­tle bit to our <kbd>mainEntityOfPage</kbd> JSON-LD:

```

{#
 # Add Article schema for the this plugin as per:
 # https://schema.org/Article
 #
 # @param article the Entry for the blog article
 #}

{# Merge in the additional properties to the Article mainEntityOfPage #}
{% set mainEntity = seomatic.jsonLd.get('mainEntityOfPage') %}
{% do mainEntity.setAttributes({
    'articleSection': article.blogCategory[0].title,
    'genre': 'Technology',
    'headline': article.title,
    'speakable': {
        'type': 'SpeakableSpecification',
        'cssSelector': [
            '.blog-wrapper',
        ],
    },
}) %}

```

This adds a few prop­er­ties, result­ing in the fol­low­ing JSON-LD struc­tured data:

```

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@type": "Article",
    "articleSection": "Insights",
    "author": {
        "@id": "https://nystudio107.com/#identity"
    },
    "copyrightHolder": {
        "@id": "https://nystudio107.com/#identity"
    },
    "copyrightYear": "2019",
    "creator": {
        "@id": "https://nystudio107.com/#creator"
    },
    "dateModified": "2019-05-28T17:33:57-04:00",
    "datePublished": "2019-05-27T08:45:00-04:00",
    "description": "Want to see a some real-world examples of JSON-LD structured data \"in the wild\"? This article has that, plus annotation.",
    "genre": "Technology",
    "headline": "Implementing Custom JSON-LD Structured Data",
    "image": {
        "@type": "ImageObject",
        "url": "https://nystudio107.com/img/blog/_1200x630_crop_center-center_82_none/json-ld-structured-data-in-the-wild.jpg"
    },
    "inLanguage": "en",
    "mainEntityOfPage": "https://nystudio107.com/blog/implementing-custom-json-ld-structured-data",
    "name": "Implementing Custom JSON-LD Structured Data",
    "publisher": {
        "@id": "https://nystudio107.com/#creator"
    },
    "speakable": {
        "@type": "SpeakableSpecification",
        "cssSelector": [
            ".blog-wrapper"
        ]
    },
    "url": "https://nystudio107.com/blog/implementing-custom-json-ld-structured-data"
}
</script>

```

Every­thing should be pret­ty straight­for­ward here in terms of the prop­er­ties we’ve added. We’re just flesh­ing things out a bit by adding some addi­tion­al information.

Prob­a­bly the coolest part is the <kbd>speakable</kbd> prop­er­ty, which lets you tell screen read­ers which text is par­tic­u­lar­ly ​“speak­able” via the [Speak­a­ble­Spec­i­fi­ca­tion](https://schema.org/SpeakableSpecification). In our case, the <kbd>.blog-wrapper</kbd> CSS class selec­tor is on the <kbd>&lt;div&gt;</kbd>s around our tex­tu­al arti­cle content.

This aids acces­si­bil­i­ty in terms of screen read­ers, and also is used by [Google Assis­tant](https://developers.google.com/search/docs/data-types/speakable) and smart devices.

Addi­tion­al things that could be done:

- Instead of just list­ing the <kbd>identity</kbd> [Orga­ni­za­tion](https://schema.org/Organization) as the author, have infor­ma­tion on the actu­al per­son who wrote it
- Add <kbd>employee</kbd> and alum­ni prop­er­ties to the <kbd>identity</kbd> & cre­ator [Orga­ni­za­tion](https://schema.org/Organization)
- Add [Inter­ac­tion­Counter](https://schema.org/InteractionCounter) via <kbd>interactionStatistic</kbd> for user com­ments on arti­cles, retweets, etc.
- Add [How­To](https://schema.org/HowTo) steps for parts of arti­cles that have spe­cif­ic steps to accom­plish a task

Go through the [schema​.org](https://schema.org/) spec­i­fi­ca­tion for the <kbd>mainEntityOfPage</kbd> schema type ([Arti­cle](https://schema.org/Article) in this case) and see what prop­er­ties it makes sense to add. Then val­i­date your page using the Google Struc­tured Data Test­ing Tool.

**Google SERP Pre­view** → [nystudio107 Arti­cle Page](https://www.google.com/search?prvw=AHHjJUN9NjCNWFoXOR9VzSbjWgydphn53Q&q=previewid%3A4efcc1f9-27a5-49c4-8bc6-b1fe589f5eb6&useragent=Mozilla%2F5.0+%28Linux%3B+Android+7.1.1%3B+Nexus+6+Build%2FN6F27M%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F66.0.3359.106+Mobile+Safari%2F537.36&filter=0&gws_rd=cr&newwindow=1&igu=1)

**Google Struc­tured Data Test­ing Tool** link → [nys​tu​dio107​.com Arti­cle Page](https://search.google.com/structured-data/testing-tool#url=https%3A%2F%2Fnystudio107.com%2Fblog%2Fimplementing-custom-json-ld-structured-data)

## nys​tu​dio107​.com Plu­g­in Page

Next up let’s have a look at the JSON-LD struc­tured data for the plu­g­in pages on [nys​tu​dio107​.com](https://nystudio107.com/), specif­i­cal­ly the [SEO­mat­ic](https://nystudio107.com/plugins/seomatic) plugin.

We’re going to skip the iden­ti­ty, cre­ator, and bread­crumbs men­tioned ear­li­er, since they’ll be the same here as well.

```

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@type": "WebApplication",
    "author": {
        "@id": "https://nystudio107.com/#identity"
    },
    "copyrightHolder": {
        "@id": "https://nystudio107.com/#identity"
    },
    "copyrightYear": "2017",
    "creator": {
        "@id": "https://nystudio107.com/#creator"
    },
    "dateModified": "2019-05-21T11:35:49-04:00",
    "datePublished": "2017-12-31T19:48:00-05:00",
    "description": "SEOmatic facilitates modern SEO best practices & implementation for Craft CMS 3. It is a turnkey SEO system that is comprehensive, powerful, and flexible.",
    "headline": "SEOmatic Craft CMS Plugin",
    "image": {
        "@type": "ImageObject",
        "url": "https://nystudio107.com/img/plugins/seomatic/_1200x630_crop_center-center_82_none/plugin-logo.png"
    },
    "inLanguage": "en",
    "mainEntityOfPage": "https://nystudio107.com/plugins/seomatic",
    "name": "SEOmatic Craft CMS Plugin",
    "publisher": {
        "@id": "https://nystudio107.com/#creator"
    },
    "url": "https://nystudio107.com/plugins/seomatic"
}
</script>

```

Here we again get some pret­ty rea­son­able defaults for our [WebAp­pli­ca­tion](https://schema.org/WebApplication) JSON-LD struc­tured data, and we make it clear that this is the <kbd>mainEntityOfPage</kbd> as dis­cussed previously.

But with a lit­tle cus­tom code from the [SEO­mat­ic](https://nystudio107.com/plugins/seomatic) Twig API we can make it much more interesting:

```

{#
 # Add WebApplication schema for the this plugin as per:
 # https://schema.org/WebApplication
 #
 # @param appInfo the appInfo Entry for the plugin
 #}

{# Offer for the plugin #}
{% set offer = seomatic.jsonLd.create({
    'type': 'Offer',
    'id': '{seomatic.site.identity.genericUrl}#plugin-offer',
    'availability': 'http://schema.org/InStock',
    'price': appInfo.pluginPrice ??? '0.00',
    'priceCurrency': 'USD',
    'priceValidUntil': now | date_modify('+1 year') | atom,
    'url': appInfo.pluginStoreLink,
}) %}

{# Merge in the additional properties to the WebApplication mainEntityOfPage #}
{% set mainEntity = seomatic.jsonLd.get('mainEntityOfPage') %}
{% set screenshotAsset = appInfo.pluginSeoImage.one() %}
{% set logoAsset = appInfo.pluginIcon.one() %}
{% do mainEntity.setAttributes({
    'name': appInfo.title,
    'applicationCategory': 'Plugin',
    'browserRequirements': 'Requires Craft CMS 3',
    'downloadUrl': appInfo.pluginStoreLink,
    'installUrl': appInfo.pluginStoreLink,
    'image': {
        'type': 'ImageObject',
        'url': logoAsset.url,
        'width': logoAsset.getWidth(),
        'height': logoAsset.getHeight(),
    },
    'screenshot': {
        'type': 'ImageObject',
        'url': screenshotAsset.url,
        'width': screenshotAsset.getWidth(),
        'height': screenshotAsset.getHeight(),
    },
    'offers': offer,
    'operatingSystem': 'Web Browser',
}) %}

{# Product for the plugin #}
{% set productConfig = mainEntity.getAttributes() | merge({
    'key': 'Plugin-Product',
    'type': 'Product',
    'brand': {
        'id': '{seomatic.site.identity.genericUrl}#identity',
    },
    'category': 'Plugin',
    'isRelatedTo': {
        'type': 'Product',
        'name': 'Craft CMS',
        'description': 'Craft is a flexible, user-friendly CMS for creating custom digital experiences on the web and beyond.',
        'slogan': 'Craft is the CMS that makes the whole team happy.',
        'url': 'https://craftcms.com/',
        'offers': {
            'type': 'Offer',
            'availability': 'http://schema.org/InStock',
            'price': '299.00',
            'priceCurrency': 'USD',
            'priceValidUntil': now | date_modify('+1 year') | atom,
            'url': 'https://craftcms.com/pricing',
        },
    },
    'logo': {
        'type': 'ImageObject',
        'url': logoAsset.url,
        'width': logoAsset.getWidth(),
        'height': logoAsset.getHeight(),
    },
    'productID': appInfo.slug,
    'sku': appInfo.slug,
    'slogan': appInfo.pluginSlogan,
}) %}
{% set product = seomatic.jsonLd.create(productConfig) %}

```

This results in a much more fleshed out [WebAp­pli­ca­tion](https://schema.org/WebApplication) JSON-LD struc­tured data, as well as cre­at­ing new [Offer](https://schema.org/Offer) and [Prod­uct](https://schema.org/Product) JSON-LD struc­tured data schemas:

```

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@type": "WebApplication",
    "applicationCategory": "Plugin",
    "author": {
        "@id": "https://nystudio107.com/#identity"
    },
    "browserRequirements": "Requires Craft CMS 3",
    "copyrightHolder": {
        "@id": "https://nystudio107.com/#identity"
    },
    "copyrightYear": "2017",
    "creator": {
        "@id": "https://nystudio107.com/#creator"
    },
    "dateModified": "2019-05-21T11:35:49-04:00",
    "datePublished": "2017-12-31T19:48:00-05:00",
    "description": "SEOmatic facilitates modern SEO best practices & implementation for Craft CMS 3. It is a turnkey SEO system that is comprehensive, powerful, and flexible.",
    "downloadUrl": "https://plugins.craftcms.com/seomatic",
    "headline": "SEOmatic Craft CMS Plugin",
    "image": {
        "@type": "ImageObject",
        "height": "250",
        "url": "https://nystudio107.com/img/plugins/seomatic/seomatic-icon.svg",
        "width": "250"
    },
    "inLanguage": "en",
    "installUrl": "https://plugins.craftcms.com/seomatic",
    "mainEntityOfPage": "https://nystudio107.com/plugins/seomatic",
    "name": "SEOmatic",
    "offers": {
        "@id": "https://nystudio107.com/#plugin-offer"
    },
    "operatingSystem": "Web Browser",
    "publisher": {
        "@id": "https://nystudio107.com/#creator"
    },
    "screenshot": {
        "@type": "ImageObject",
        "height": "500",
        "url": "https://nystudio107.com/img/plugins/seomatic/plugin-logo.png",
        "width": "450"
    },
    "url": "https://nystudio107.com/plugins/seomatic"
}
</script>

```

Here we are once again fill­ing in a num­ber of prop­er­ties that make sense for a [WebAp­pli­ca­tion](https://schema.org/WebApplication) (which is the most cor­rect fit for a plu­g­in for a web-based CMS).

We’ve added in prop­er­ties like <kbd>downloadUrl</kbd>, <kbd>installUrl</kbd>, <kbd>screenshot</kbd>, and more inter­est­ing­ly <kbd>offers</kbd> via the <kbd>"@id": "https://nystudio107.com/#plugin-offer"</kbd> [Node Iden­ti­fi­er](https://www.w3.org/TR/2014/REC-json-ld-20140116/#node-identifiers):

```

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@id": "https://nystudio107.com/#plugin-offer",
    "@type": "Offer",
    "availability": "http://schema.org/InStock",
    "price": "99",
    "priceCurrency": "USD",
    "priceValidUntil": "2020-05-28T19:03:39-04:00",
    "url": "https://plugins.craftcms.com/seomatic"
}
</script>

```

Since some of our plu­g­ins are avail­able for sale, we can indi­cate that via the [Offer](https://schema.org/Offer) schema that lists a <kbd>price</kbd>, <kbd>priceCurrency</kbd>, <kbd>availability</kbd>, etc.

**Pro tip:** if your prod­uct is free, you should indi­cate that by hav­ing all of the above prop­er­ties, but set­ting the <kbd>price</kbd> to <kbd>0</kbd>.

We imple­ment­ed this with a [Node Iden­ti­fi­er](https://www.w3.org/TR/2014/REC-json-ld-20140116/#node-identifiers) so that we can ref­er­ence it from both the <kbd>WebApplication</kbd> and <kbd>Product</kbd> JSON-LD:

```

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@type": "Product",
    "brand": {
        "@id": "https://nystudio107.com/#identity"
    },
    "category": "Plugin",
    "description": "SEOmatic facilitates modern SEO best practices & implementation for Craft CMS 3. It is a turnkey SEO system that is comprehensive, powerful, and flexible.",
    "image": {
        "@type": "ImageObject",
        "height": "250",
        "url": "https://nystudio107.com/img/plugins/seomatic/seomatic-icon.svg",
        "width": "250"
    },
    "isRelatedTo": {
        "@type": "Product",
        "description": "Craft is a flexible, user-friendly CMS for creating custom digital experiences on the web and beyond.",
        "name": "Craft CMS",
        "offers": {
            "@type": "Offer",
            "availability": "http://schema.org/InStock",
            "price": "299.00",
            "priceCurrency": "USD",
            "priceValidUntil": "2020-11-20T09:07:28-05:00",
            "url": "https://craftcms.com/pricing"
        },
        "slogan": "Craft is the CMS that makes the whole team happy.",
        "url": "https://craftcms.com/"
    },
    "logo": {
        "@type": "ImageObject",
        "height": "250",
        "url": "https://nystudio107.com/img/plugins/seomatic/seomatic-icon.svg",
        "width": "250"
    },
    "mainEntityOfPage": "https://nystudio107.com/plugins/seomatic",
    "name": "SEOmatic",
    "offers": {
        "@id": "https://nystudio107.com/#plugin-offer"
    },
    "productID": "seomatic",
    "sku": "seomatic",
    "url": "https://nystudio107.com/plugins/seomatic"
}
</script>

```

We took all of the prop­er­ties from our [WebAp­pli­ca­tion](https://schema.org/WebApplication), and used them to cre­ate a new [Prod­uct](https://schema.org/Product) JSON-LD schema (SEO­mat­ic strips out any prop­er­ties that don’t exist in the new schema type).

Then we added a few things spe­cif­ic to a <kbd>Product</kbd> such as the <kbd>isRelatedTo</kbd> prop­er­ty, relat­ing it to [Craft CMS](https://craftcms.com/) (since our plu­g­ins require it).

So why cre­ate a new <kbd>Product</kbd> schema? Because once again, it’s a type that [Google specif­i­cal­ly sup­ports](https://developers.google.com/search/docs/data-types/product).

This is also some­thing that can result in your <kbd>Product</kbd> appear­ing on the Search Engine Results Page (SERP) with a thumb­nail screen­shot, reviews, rat­ings, and oth­er fea­tures that [encour­age engage­ment](https://developers.google.com/search/docs/guides/search-gallery).

Addi­tion­al things that could be done:

- The [Prod­uct](https://schema.org/Product) should have an <kbd>aggregateRating</kbd>
- Adding legit­i­mate user-sub­mit­ted [Reviews](https://schema.org/Review)

**Google SERP Pre­view** → [nystudio107 Plu­g­in Page](https://www.google.com/search?prvw=AHHjJUNxHw2bA-aau7gmXikpjRMhEgsiag&q=previewid%3A5f95e05e-a0c2-4077-8b49-605450456018&useragent=Mozilla%2F5.0+%28Linux%3B+Android+7.1.1%3B+Nexus+6+Build%2FN6F27M%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F66.0.3359.106+Mobile+Safari%2F537.36&filter=0&gws_rd=cr&newwindow=1&igu=1)

**Google Rich Results Test** → [nystudio107 Plu­g­in Page](https://search.google.com/test/rich-results?id=oZLT8J4GjJ_S7OLzILcqkg)

**Google Struc­tured Data Test­ing Tool** link → [nys​tu​dio107​.com Plu­g­in Page](https://search.google.com/structured-data/testing-tool#url=https%3A%2F%2Fnystudio107.com%2Fplugins%2Fseomatic)

## dev​Mode​.fm About Page

The dev​Mode​.fm About page serves as a good exam­ple of how we can imple­ment an [FAQ­Page](https://schema.org/FAQPage) schema​.org type with [Ques­tions](https://schema.org/Question) & [Answers](https://schema.org/Answer) that can show up on the Google Search Engine Results Page (SERP). Check out the [Google Devel­op­ers Search FAQ page](https://developers.google.com/search/docs/data-types/faqpage) for details.

```

{
  "@context": "http://schema.org",
  "@type": "FAQPage",
  "author": {
    "@id": "https://devmode.fm/#identity"
  },
  "copyrightHolder": {
    "@id": "https://devmode.fm/#identity"
  },
  "copyrightYear": "2017",
  "creator": {
    "@id": "https://devmode.fm/#creator"
  },
  "dateModified": "2019-08-19T11:19:14-04:00",
  "datePublished": "2017-12-11T13:44:33-05:00",
  "description": "devMode.fm is a bi-weekly podcast dedicated to the tools, techniques, and technologies used in modern web development. Each episode, we have a cadre of hosts discussing the latest hotness, pet peeves, and technologies we use every day. We all come from a Craft CMS background, but we'll be focusing on other cool frontend development technologies as well.",
  "headline": "About the podcast",
  "image": {
    "@type": "ImageObject",
    "url": "https://devmode.fm/assets/site/_1200x630_crop_center-center_82_none/devmode_light-itunes.jpg?mtime=1515536278"
  },
  "inLanguage": "en-us",
  "mainEntity": [
    {
      "@type": "Question",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "<p>devMode.fm is a bi-weekly podcast dedicated to the tools, techniques, and technologies used in modern web development. Each episode, we have a cadre of hosts discussing the latest hotness, pet peeves, and technologies we use every day. We all come from a Craft CMS background, but we'll be focusing on other cool frontend development technologies as well.</p>"
      },
      "name": "What is the devMode.fm podcast all about?"
    },
    {
      "@type": "Question",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "<p>It sure is! The source code for this website is MIT licensed, and can be found on Github: <a href=\"https://github.com/nystudio107/devmode\" target=\"_blank\" rel=\"noreferrer noopener\">nystudio107/devmode</a>.</p>"
      },
      "name": "Is the source code to the devMode.fm website available?"
    },
    {
      "@type": "Question",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "<p>The devMode.fm website is built with:</p><a href=\"https://craftcms.com/\" target=\"_blank\">Craft 3 CMS</a>, <a href=\"https://tailwindcss.com/\" target=\"_blank\">Tailwind CSS</a>, <a href=\"https://vuejs.org/\" target=\"_blank\">Vue.js</a>, <a href=\"https://webpack.js.org/\" target=\"_blank\">webpack</a>, <a href=\"https://www.nginx.com/\" target=\"_blank\">Nginx</a>, <a href=\"https://www.postgresql.org\" target=\"_blank\">PostgreSQL</a>, <a href=\"https://forge.laravel.com\" target=\"_blank\">Forge</a>, <a href=\"https://github.com/nystudio107/craft\" target=\"_blank\">nystudio107/craft</a>"
      },
      "name": "What technologies were used building the devMode.fm website?"
    }
  ],
  "mainEntityOfPage": "https://devmode.fm/about",
  "name": "About the podcast",
  "publisher": {
    "@id": "https://devmode.fm/#creator"
  },
  "url": "https://devmode.fm/about"
}

```

Here we have set the **Main Enti­ty of Page** to **FAQ­Page** in the SEO­mat­ic set­tings in the CP, and then we’re using a bit of Twig to read FAQs from a matrix block that has the fields <kbd>question</kbd>, <kbd>answer</kbd>, and <kbd>links</kbd>:

```

{#
 # Add FAQPage schema for the this page as per:
 # https://schema.org/FAQPage
 #
 # @param faqs the FAQs matrix blocks
 # @param showInfo the showInfo Global for the show
 #}

{% set faqsArray = [] %}
{% for faq in faqs.all() %}
    {% set faqLinks = '' %}
    {% for link in faq.links %}
        {%- set linkHtml -%}
            <a href="{{ link.linkUrl }}" target="_blank">{{ link.linkText }}</a>
        {%- endset -%}
        {% set faqLinks = faqLinks ~ linkHtml ~ (loop.last ? '' : ', ') %}
    {% endfor %}
    {% set faqsArray = faqsArray | merge([seomatic.jsonLd.create({
        'type': 'Question',
        'name': faq.question,
        'acceptedAnswer': {
            'type': 'Answer',
            'text': (faq.answer ~ faqLinks),
        },
    }, false)]) %}
{% endfor %}

{# Merge in the additional properties to the FAQpage mainEntityOfPage #}
{% set mainEntity = seomatic.jsonLd.get('mainEntityOfPage') %}
{% do mainEntity.setAttributes({
    'mainEntity': faqsArray,
}) %}

```

In this case we set the <kbd>mainEntity</kbd> prop­er­ty to an array of [Ques­tion JSON-LD](https://schema.org/Question) types, each of which has an [Answer JSON-LD](https://schema.org/Answer) type embed­ded in the <kbd>acceptedAnswer</kbd> field.

This is some­thing that can result in your <kbd>FAQPage</kbd> Ques­tions & Answers appear­ing on the Search Engine Results Page (SERP).

**Google SERP Pre­view** → [dev​Mode​.fm About Page](https://search.google.com/test/rich-results?view=search-preview&id=UTxlLUl7dhK31iEYkDedmQ)

**Google Rich Results Test** → [dev​Mode​.fm About Page](https://search.google.com/test/rich-results?id=UTxlLUl7dhK31iEYkDedmQ)

**Google Struc­tured Data Test­ing Tool** link → [dev​Mode​.fm About Page](https://search.google.com/structured-data/testing-tool#url=https%3A%2F%2Fdevmode.fm%2Fabout)

## dev​Mode​.fm Episode Page

Final­ly let’s have a look at the JSON-LD struc­tured data for the episode pages on [dev​Mode​.fm](https://devMode.fm), in this case the [web­pack inside & out with Sean Larkin](https://devmode.fm/episodes/webpack-inside-out-with-sean-larkin) episode.

We’re going to skip the iden­ti­ty, cre­ator, and bread­crumbs men­tioned ear­li­er. Even though the val­ues will be dif­fer­ent for this site, the same prop­er­ties are filled in.

```

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@type": "RadioEpisode",
    "author": {
        "@id": "https://devmode.fm/#identity"
    },
    "copyrightHolder": {
        "@id": "https://devmode.fm/#identity"
    },
    "copyrightYear": "2019",
    "creator": {
        "@id": "https://devmode.fm/#creator"
    },
    "dateModified": "2019-05-27T09:10:22-04:00",
    "datePublished": "2019-05-27T09:05:00-04:00",
    "description": "In this episode, we talk to webpack core maintainer Sean Larkin about what webpack is, who it's intended for, and where it's going in the future!\n\nWe discuss the serendipitous history of how Sean came to be a webpack core maintainer, and how his job at Microsoft came about as a result of it.\n\nJoin by guest host Jake Dohm, we then go on to discuss a whole lot of gritty technical detail of how webpack works, go through the various terminology, talk about Web Assembly, and what the future holds for webpack 5 and beyond.\n\nSean also drops some truth-bombs about CSS being flawed, and browser makers conspiring to kill off webpack. Tune in for the good stuff!",
    "headline": "webpack inside & out with Sean Larkin",
    "image": {
        "@type": "ImageObject",
        "url": "https://devmode.fm/assets/site/_1200x630_crop_center-center_82_none/devmode_light-itunes.jpg"
    },
    "inLanguage": "en-us",
    "mainEntityOfPage": "https://devmode.fm/episodes/webpack-inside-out-with-sean-larkin",
    "name": "webpack inside & out with Sean Larkin",
    "publisher": {
        "@id": "https://devmode.fm/#creator"
    },
    "url": "https://devmode.fm/episodes/webpack-inside-out-with-sean-larkin"
}
</script>

```

Once again we have a pret­ty good start­ing point for our [RadioEpisode](https://schema.org/RadioEpisode) schema (there is no [Pod­cast](https://github.com/schemaorg/schemaorg/issues/373) schema yet) that is our <kbd>mainEntityOfPage</kbd>. But we can add a bunch more infor­ma­tion in this case to describe the series that this pod­cast belongs to, and more.

Once again we’ll use the [SEO­mat­ic](https://nystudio107.com/plugins/seomatic) Twig API to do so:

```

{#
 # Add audio schema for the this episode as per:
 # https://schema.org/RadioEpisode
 #
 # @param episode the episode Entry for the current page
 # @param fileInfo summary of the episode media file info as per:
 # https://github.com/nystudio107/craft-transcoder#getting-information-about-a-videoaudio-file
 # @param showInfo the showInfo Global for the show
 # @param audioUrl the URL to the audio for this episode, wrapped in the
 # PodTrac.com tracking redirect
 #}

{% from "_partials/macros.twig" import addPersonArray %}
{% from "_partials/macros.twig" import addMentionsArray %}

{# RadioSeries for the episode to belong to #}
{% set showImage = showInfo.showImage.one() %}
{% set radioSeries = seomatic.jsonLd.create({
    'type': 'RadioSeries',
    'id': '{seomatic.site.identity.genericUrl}#radio-series',
    'name': showInfo.showTitle,
    'description': showInfo.showDescription,
    'url': showInfo.showUrl,
    'mainEntityOfPage': siteUrl,
    'inLanguage': '{seomatic.meta.language}',
    'copyrightHolder': {
        'id': '{seomatic.site.identity.genericUrl}#identity',
    },
    'author': {
        'id': '{seomatic.site.identity.genericUrl}#identity',
    },
    'creator': {
        'id': '{seomatic.site.creator.genericUrl}#creator',
    },
    'image': {
        'type': 'ImageObject',
        'url': showImage.url,
        'width': showImage.width,
        'height': showImage.height,
    },
}) %}

{# Person array of the episode hosts #}
{% do addPersonArray(radioSeries, 'actor', craft.users.group("hosts").all()) %}
{# Person array of the episode directors #}
{% do addPersonArray(radioSeries, 'director', craft.users.group("owners").all()) %}

{# AudioObject for the episode #}
{% set audio = seomatic.jsonLd.create({
    'type': 'AudioObject',
    'bitrate': '64k',
    'contentSize': fileInfo.size,
    'contentUrl': audioUrl,
    'duration': fileInfo.duration,
    'embedUrl': siteUrl("/player-card/#{episode.slug}"),
    'encodingFormat': 'audio/mpeg',
    'partOfSeries': {
        'id': '{seomatic.site.identity.genericUrl}#radio-series',
    },
    'productionCompany': {
        'id': '{seomatic.site.creator.genericUrl}#creator',
    },
    'uploadDate': episode.postDate | rss,
}, false) %}

{# Merge in the additional properties to the RadioEpisode mainEntityOfPage #}
{% set mainEntity = seomatic.jsonLd.get('mainEntityOfPage') %}
{% do mainEntity.setAttributes({
    'audio': audio,
    'episodeNumber': episode.episodeNumber,
    'genre': showInfo.showGenre,
    'isAccessibleForFree': true,
    'productionCompany': {
        'id': '{seomatic.site.creator.genericUrl}#creator',
    },
}) %}

{# Person array of the episode hosts #}
{% do addPersonArray(mainEntity, 'actor', episode.episodeHosts.all()) %}
{# Person array of the episode directors #}
{% do addPersonArray(mainEntity, 'director', craft.users.group("owners").all()) %}
{# Thing array of the episode mentions #}
{% do addMentionsArray(mainEntity, 'mentions', episode.episodeReferenceLinks) %}

```

This is get­ting a whole lot more com­plex, and we’re even includ­ing two macros to help gen­er­ate the addi­tion­al JSON-LD struc­tured data:

```

{# Add a Person array from `users` to the `property` of `jsonLd` #}
{% macro addPersonArray(jsonLd, property, users) %}
    {% set usersArray = [] %}
    {% for user in users %}
        {% set usersArray = usersArray | merge([seomatic.jsonLd.create({
            'type': 'Person',
            'affiliation': user.profileCompany,
            'description': user.profileBio,
            'jobTitle': user.profileTitle,
            'familyName': user.lastName,
            'givenName': user.firstName,
            'name': user.fullName,
            'sameAs': [
                user.profileTwitterUrl,
                user.profileGithubUrl,
            ],
            'url': user.profileUrl,
        }, false)]) %}
    {% endfor %}
    {% do jsonLd.setAttributes({
        (property): usersArray
    }) %}
{% endmacro addPersonArray %}

{# Add a Thing array from `mentions` to the `property` of `jsonLd` #}
{% macro addMentionsArray(jsonLd, property, mentions) %}
    {% set mentionsArray = [] %}
    {% for mention in mentions %}
        {% set mentionsArray = mentionsArray | merge([seomatic.jsonLd.create({
            'type': 'Thing',
            'name': mention.linkName,
            'url': mention.linkUrl,
        }, false)]) %}
    {% endfor %}
    {% do jsonLd.setAttributes({
        (property): mentionsArray
    }) %}
{% endmacro addMentionsArray %}

```

I think the eas­i­est way to under­stand what the code above does is to look at the result­ing out­put, so let’s do just that, and look at our [RadioEpisode](https://schema.org/RadioEpisode) <kbd>mainEntityOfPage</kbd> JSON-LD now:

```

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@type": "RadioEpisode",
    "actor": [
        {
            "@type": "Person",
            "affiliation": "nystudio107",
            "description": "Andrew Welch has been in the tech business since he was 15 years old. After a stint running a software company for a couple of decades, he's now immersing himself in doing consulting to help businesses use technology effectively.",
            "familyName": "Welch",
            "givenName": "Andrew",
            "jobTitle": "Demolitions Expert",
            "name": "Andrew Welch",
            "sameAs": [
                "https://twitter.com/nystudio107",
                "https://github.com/nystudio107"
            ],
            "url": "https://nystudio107.com"
        },
        {
            "@type": "Person",
            "affiliation": "MDD",
            "description": "Jonathan is principal/CEO of MDD, a small web agency based in Atlanta, Georgia. He has been developing for the web since 2005 and Craft CMS since 2013. He used to have free time and cool hobbies until his second child arrived on December 6, 2017. He loves his work and his family.",
            "familyName": "Melville",
            "givenName": "Jonathan",
            "jobTitle": "Jonathan Melville",
            "name": "Jonathan Melville",
            "sameAs": [
                "https://twitter.com/jonmelville",
                "https://github.com/jonathanmelville"
            ],
            "url": "https://codemdd.io"
        }
    ],
    "audio": {
        "@context": "http://schema.org",
        "@type": "AudioObject",
        "bitrate": "64k",
        "contentSize": "72969149",
        "contentUrl": "https://devmode.fm/transcoder/devmode0041_64kbps_22050_1c.mp3",
        "duration": "4560.535510",
        "embedUrl": "https://devmode.fm/player-card/webpack-inside-out-with-sean-larkin",
        "encodingFormat": "audio/mpeg",
        "productionCompany": {
            "@id": "https://nystudio107.com/#creator"
        },
        "uploadDate": "Mon, 27 May 2019 09:05:00 -0400"
    },
    "author": {
        "@id": "https://devmode.fm/#identity"
    },
    "copyrightHolder": {
        "@id": "https://devmode.fm/#identity"
    },
    "copyrightYear": "2019",
    "creator": {
        "@id": "https://devmode.fm/#creator"
    },
    "dateModified": "2019-05-27T09:10:22-04:00",
    "datePublished": "2019-05-27T09:05:00-04:00",
    "description": "In this episode, we talk to webpack core maintainer Sean Larkin about what webpack is, who it's intended for, and where it's going in the future!\n\nWe discuss the serendipitous history of how Sean came to be a webpack core maintainer, and how his job at Microsoft came about as a result of it.\n\nJoin by guest host Jake Dohm, we then go on to discuss a whole lot of gritty technical detail of how webpack works, go through the various terminology, talk about Web Assembly, and what the future holds for webpack 5 and beyond.\n\nSean also drops some truth-bombs about CSS being flawed, and browser makers conspiring to kill off webpack. Tune in for the good stuff!",
    "director": [
        {
            "@type": "Person",
            "affiliation": "nystudio107",
            "description": "Andrew Welch has been in the tech business since he was 15 years old. After a stint running a software company for a couple of decades, he's now immersing himself in doing consulting to help businesses use technology effectively.",
            "familyName": "Welch",
            "givenName": "Andrew",
            "jobTitle": "Demolitions Expert",
            "name": "Andrew Welch",
            "sameAs": [
                "https://twitter.com/nystudio107",
                "https://github.com/nystudio107"
            ],
            "url": "https://nystudio107.com"
        },
        {
            "@type": "Person",
            "affiliation": "Mildly Geeky",
            "description": "Patrick Harrington has been obsessed with the web ever since picking up a copy of HTML for Dummies at age 13. After working for larger Boston-area agencies, he started his own company in 2011 and specializes in Craft CMS.",
            "familyName": "Harrington",
            "givenName": "Patrick",
            "jobTitle": "President, Founder",
            "name": "Patrick Harrington",
            "sameAs": [
                "https://twitter.com/p_harrington83",
                "https://github.com/mildlygeeky"
            ],
            "url": "https://mildlygeeky.com"
        }
    ],
    "episodeNumber": 41,
    "genre": "Technology",
    "headline": "webpack inside & out with Sean Larkin",
    "image": {
        "@type": "ImageObject",
        "url": "https://devmode.fm/assets/site/_1200x630_crop_center-center_82_none/devmode_light-itunes.jpg"
    },
    "inLanguage": "en-us",
    "isAccessibleForFree": true,
    "mainEntityOfPage": "https://devmode.fm/episodes/webpack-inside-out-with-sean-larkin",
    "mentions": [
        {
            "@type": "Thing",
            "name": "webpack concepts",
            "url": "https://webpack.js.org/concepts/"
        },
        {
            "@type": "Thing",
            "name": "webpack in Wikipedie",
            "url": "https://en.wikipedia.org/wiki/Webpack"
        },
        {
            "@type": "Thing",
            "name": "Webpack 4 Fundamentals videos",
            "url": "https://frontendmasters.com/courses/webpack-fundamentals/"
        },
        {
            "@type": "Thing",
            "name": "Webpack — The Confusing Parts",
            "url": "https://medium.com/@rajaraodv/webpack-the-confusing-parts-58712f8fcad9"
        },
        {
            "@type": "Thing",
            "name": "An Annotated webpack 4 Config for Frontend Web Development",
            "url": "https://nystudio107.com/blog/an-annotated-webpack-4-config-for-frontend-web-development"
        },
        {
            "@type": "Thing",
            "name": "A tale of Webpack 4 and how to finally configure it in the right way.",
            "url": "https://hackernoon.com/a-tale-of-webpack-4-and-how-to-finally-configure-it-in-the-right-way-4e94c8e7e5c1"
        }
    ],
    "name": "webpack inside & out with Sean Larkin",
    "productionCompany": {
        "@id": "https://nystudio107.com/#creator"
    },
    "publisher": {
        "@id": "https://devmode.fm/#creator"
    },
    "url": "https://devmode.fm/episodes/webpack-inside-out-with-sean-larkin"
}
</script>

```

I’ll skip over some of the added prop­er­ties, and focus on the more inter­est­ing bits.

The <kbd>addPersonArray()</kbd> macro is used to add in the <kbd>director</kbd>s of the show, and it is also re-used to add in the <kbd>actor</kbd>s who par­tic­i­pate in the episode as well. Adding this type of rela­tion­ship infor­ma­tion real­ly helps Google fig­ure out the con­nec­tions between peo­ple and their works on the web.

Then we’ve also added an [AudioOb­ject](https://schema.org/AudioObject) JSON-LD object in the <kbd>audio</kbd> prop­er­ty that rep­re­sents the episode audio itself, and oth­er var­i­ous asso­ci­at­ed meta infor­ma­tion about the audio.

Final­ly, all of the ​“show links” we dis­play on the web­page are added in the <kbd>mentions</kbd> prop­er­ty via the <kbd>addMentionsArray()</kbd> macro, so every­thing we men­tion on the show is linked to.

Final­ly, we add a new [RadioSeries](https://schema.org/RadioSeries) JSON-LD struc­tured data schema:

```

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@id": "https://devmode.fm/#radio-series",
    "@type": "RadioSeries",
    "actor": [
        {
            "@type": "Person",
            "affiliation": "nystudio107",
            "description": "Andrew Welch has been in the tech business since he was 15 years old. After a stint running a software company for a couple of decades, he's now immersing himself in doing consulting to help businesses use technology effectively.",
            "familyName": "Welch",
            "givenName": "Andrew",
            "jobTitle": "Demolitions Expert",
            "name": "Andrew Welch",
            "sameAs": [
                "https://twitter.com/nystudio107",
                "https://github.com/nystudio107"
            ],
            "url": "https://nystudio107.com"
        },
        {
            "@type": "Person",
            "affiliation": "Hypatia Industries",
            "description": "Earl Johnston is a freelance web developer operating under the Hypatia Industries banner. He specializes in high fives and bourbon.",
            "familyName": "Johnston",
            "givenName": "Earl",
            "jobTitle": "Professor Doctor",
            "name": "Earl Johnston",
            "url": "http://www.hypatia-industries.com"
        },
        {
            "@type": "Person",
            "affiliation": "A Bright Color",
            "description": "Lauren is a developer with a background in design, hailing from Toledo, Ohio. Her focus: make understanding code scalability easier, to help create performant and accessible experiences. Outside of work: playing with her MIDI controller, finding the best shows, and cycling around the streets of Neukölln.",
            "familyName": "Dorman",
            "givenName": "Lauren",
            "jobTitle": "Development",
            "name": "Lauren Dorman",
            "sameAs": [
                "https://twitter.com/laurendorman?lang=en",
                "https://github.com/laurendorman"
            ],
            "url": "https://laurendorman.io/about/"
        },
        {
            "@type": "Person",
            "affiliation": "MDD",
            "description": "Jonathan is principal/CEO of MDD, a small web agency based in Atlanta, Georgia. He has been developing for the web since 2005 and Craft CMS since 2013. He used to have free time and cool hobbies until his second child arrived on December 6, 2017. He loves his work and his family.",
            "familyName": "Melville",
            "givenName": "Jonathan",
            "jobTitle": "Jonathan Melville",
            "name": "Jonathan Melville",
            "sameAs": [
                "https://twitter.com/jonmelville",
                "https://github.com/jonathanmelville"
            ],
            "url": "https://codemdd.io"
        },
        {
            "@type": "Person",
            "affiliation": "Newlevant",
            "description": "Marion has been writing software for over 40 years. She may not have seen it all, but she's seen many things, most of them more than once. Marion is the queen of twig macros, and the second most famous Newlevant.",
            "familyName": "Newlevant",
            "givenName": "Marion",
            "jobTitle": "Marion Newlevant",
            "name": "Marion Newlevant",
            "sameAs": [
                "https://twitter.com/marionnewlevant",
                "https://github.com/marionnewlevant/"
            ],
            "url": "http://marion.newlevant.com"
        },
        {
            "@type": "Person",
            "affiliation": "Working Concept",
            "description": "Matt's a web developer with a design background that's been building websites for the past decade. He loves learning, connecting things, and helping businesses solve multi-faceted, internet-shaped problems.",
            "familyName": "Stein",
            "givenName": "Matt",
            "jobTitle": "President/Designer/Developer",
            "name": "Matt Stein",
            "sameAs": [
                "https://twitter.com/mattrambles",
                "https://github.com/mattstein"
            ],
            "url": "https://workingconcept.com"
        },
        {
            "@type": "Person",
            "affiliation": "Build For Humans",
            "description": "Michael runs a small dev team in Texas. Code is his happy place. He wants to be a teacher when he grows up. When not writing code, he cooks with friends, travels all over the place, sings in the shower, and works out with the circus.",
            "familyName": "Rog",
            "givenName": "Michael",
            "jobTitle": "Michael Rog",
            "name": "Michael Rog",
            "sameAs": [
                "https://twitter.com/michaelrog",
                "https://github.com/TopShelfCraft"
            ],
            "url": "https://michaelrog.com"
        },
        {
            "@type": "Person",
            "affiliation": "Mildly Geeky",
            "description": "Patrick Harrington has been obsessed with the web ever since picking up a copy of HTML for Dummies at age 13. After working for larger Boston-area agencies, he started his own company in 2011 and specializes in Craft CMS.",
            "familyName": "Harrington",
            "givenName": "Patrick",
            "jobTitle": "President, Founder",
            "name": "Patrick Harrington",
            "sameAs": [
                "https://twitter.com/p_harrington83",
                "https://github.com/mildlygeeky"
            ],
            "url": "https://mildlygeeky.com"
        }
    ],
    "author": {
        "@id": "https://devmode.fm/#identity"
    },
    "copyrightHolder": {
        "@id": "https://devmode.fm/#identity"
    },
    "creator": {
        "@id": "https://nystudio107.com/#creator"
    },
    "description": "devMode.fm is a bi-weekly podcast dedicated to the tools, techniques, and technologies used in modern web development. Each episode, we have a cadre of hosts discussing the latest hotness, pet peeves, and technologies we use every day. We all come from a Craft CMS background, but we'll be focusing on other cool frontend development technologies as well.",
    "director": [
        {
            "@type": "Person",
            "affiliation": "nystudio107",
            "description": "Andrew Welch has been in the tech business since he was 15 years old. After a stint running a software company for a couple of decades, he's now immersing himself in doing consulting to help businesses use technology effectively.",
            "familyName": "Welch",
            "givenName": "Andrew",
            "jobTitle": "Demolitions Expert",
            "name": "Andrew Welch",
            "sameAs": [
                "https://twitter.com/nystudio107",
                "https://github.com/nystudio107"
            ],
            "url": "https://nystudio107.com"
        },
        {
            "@type": "Person",
            "affiliation": "Mildly Geeky",
            "description": "Patrick Harrington has been obsessed with the web ever since picking up a copy of HTML for Dummies at age 13. After working for larger Boston-area agencies, he started his own company in 2011 and specializes in Craft CMS.",
            "familyName": "Harrington",
            "givenName": "Patrick",
            "jobTitle": "President, Founder",
            "name": "Patrick Harrington",
            "sameAs": [
                "https://twitter.com/p_harrington83",
                "https://github.com/mildlygeeky"
            ],
            "url": "https://mildlygeeky.com"
        }
    ],
    "image": {
        "@type": "ImageObject",
        "height": 1800,
        "url": "http://devmode.test/assets/site/devmode_light-itunes.jpg",
        "width": 1800
    },
    "inLanguage": "en-us",
    "mainEntityOfPage": "http://devmode.test/",
    "name": "devMode.fm",
    "url": "https://devmode.fm/"
}
</script>

```

This [RadioSeries](https://schema.org/RadioSeries) schema gives the indi­vid­ual episode con­text in terms of what series it belongs to, and also re-uses the <kbd>addPersonArray()</kbd> macro to add all of the peo­ple who have ever been on the show as a host as part of the [RadioSeries](https://schema.org/RadioSeries).

Addi­tion­al things that could be done:

- Tele­phone num­bers could be includ­ed for the [Orga­ni­za­tion](https://schema.org/Organization) & [Per­son](https://schema.org/Person) schemas
- Add infor­ma­tion on the guests to the <kbd>actor</kbd> prop­er­ty (this data just isn’t in the CMS currently)
- A [Sitelinks Search­box](https://developers.google.com/search/docs/data-types/sitelinks-searchbox) could be added

**Google Struc­tured Data Test­ing Tool** link → [dev​Mode​.fm Episode Page](https://search.google.com/structured-data/testing-tool#url=https%3A%2F%2Fdevmode.fm%2Fepisodes%2Fwebpack-inside-out-with-sean-larkin)

## Fin.

These exam­ples show sev­er­al real-world uses of JSON-LD struc­tured data, but even here, more could be done!

![Thats all folks](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x675_crop_center-center_82_line/thats-all-folks.jpg)

I think it makes sense to get the low-hang­ing fruit first, and address the con­nec­tions & con­text that might be dif­fi­cult for Google to deter­mine on its own.

Hope­ful­ly these anno­tat­ed exam­ples of how JSON-LD struc­tured data is actu­al­ly used in the wild will be help­ful to you.

Cre­ate your own cus­tom JSON-LD struc­tured data by prag­mat­i­cal­ly pick­ing the schemas and prop­er­ties that ​“best fit” your web­site, pay­ing a lit­tle extra spe­cial atten­tion to the schemas that Google consumes.

Enjoy your struc­tured data!

## Further Reading

_If you want to be notified about new articles, follow [nystudio107](https://twitter.com/nystudio107) on Twitter._

<small>Copyright ©2020 nystudio107. Designed by nystudio107</small>