Eleventy Base Blog v9 + Structured Data

How to Add WebPage Schema to All Pages on the Website

All pages on a website are WebPages. Let's reflect this in our schema.

But first lets talk about how to generate systematically URLs for "@id" attributes.

Generating ID attributes for schema entities

Generating "@id" attributes systematically is important because you need them to link different entities in the JSON-LD "@graph" together.

Ideally you want a system to generate unique IDs that you can remember as a human.

Uniqueness is important because otherwise it will cause parsing errors. "@id" values need to only be unique in the context of the document in our case the web page.

Note:

Many Schema gurus claim that "@id" attributes need tg be unique in the scope of a website or even world globally.

The JSON-LD spec says otherwise!

I recommend to use URLs for IDs in schema markup. More specifically, I use absolute URLs, not just fragments.

For most IDs I use the URL that is the entity's "home" + a URL fragment indicating the type. For example I use:

There is no hard rule, you can use what ever you want I just developed these as best practice and it served me well over many websites.

A Helper filter to generate these IDs

To make generating the absolute URLs for "@id" attributes simpler and less error prone, I use the following filter function.

	eleventyConfig.addFilter("constructID", (relative_url, base, fragment) => {
		var u = new URL( HtmlBasePlugin.applyBaseToUrl(fragment, base, {
			pathPrefix: eleventyConfig.pathPrefix,
			pageUrl: relative_url
		}));
		return u.href;
	});

I define the URLs by via the preprocessing of the front matter with Nunjucks. For example:

	'{{ page.url | constructID(metadata.url, "#webpage") }}'

I added the WebPage schema into content/content.11data.js as the most appropriate place.

	eleventyComputed: {
		schemaorg: {
			"@context": "https://schema.org",
			"@graph": [
				{
					"@type": "WebPage",
					"@id": '{{ page.url | constructID(metadata.url, "#webpage") }}',
					name: (data) => data.title,
					description: (data) => data.description,
					url: '{{ page.url | constructID(metadata.url, "") }}',
					isPartOf: {
						"@type": "WebSite",
						"@id": '{{ "/" | constructID(metadata.url, "#website") }}',
					},
					author: {
						"@type": "Person",
						"@id": '{{ "/" | constructID(metadata.url, "#person_kaj_kandler") }}'
					}
				}
			]
		}
	}

As you can see I connect all "WebPage" entities to the []"WebSite" entity created as global schema](/blog/adding_global_schema_to_eleventy.md).

Furthermore, I reuse the person already declared globally as the author of all pages. You can adjust this to your needs.

Do Not Forget to Validate your Schema

As a reminder, do not forget to validate every step your schema markup.

Note:

Pay attention to the connecting "@id" attributes! Best practice is to copy paste the "@type" and "@id" lines from the definition of an object to the reference.