Schema.org
Schema.org metadata tells search engines how to understand the content of your website so they can generate rich, interactive search results for your website. The search engines will typically prioritize these results over standard ones.
If you run a news website, for example, you can define article titles, summaries, and photos. Or if your website contains restaurant locations, you can provide restaurant hours, addresses, and menus.
Schema.org defines standardized sets of attributes for almost any kind of metadata you want to share with search engines.
This guide demonstrates a few kinds of schemas to show how you can connect your website’s content to whichever schema you need.
Find a specific schema definition using Schema.org’s full list of schemas.
🏃♀️ Want to see the final result in action?
Create a new Prismic project with everything described in this guide with the following commands:
npx degit prismicio-community/how-to-nextjs-schema-org how-to-nextjs-schema-org
cd how-to-nextjs-schema-org
npx @slicemachine/init
Much of the content needed by search engines already exists in your content models. Fields for a blog post’s title and author, for example, are likely already included. This content can be used in both the visual parts of your website and the invisible metadata.
Any metadata fields that don’t exist will need to be added. When necessary, fields can be labeled with “(Schema.org)” to let content writers know which field values won’t be visible on your website.
Metadata must be written using specific attribute names. Google’s schema-dts
can be used with JSDoc or TypeScript to more accurately write schemas. Using the package gives you auto-completion in your code editor and verifies you are passing the correct data.
Install schema-dts
with the following command:
- npm
- Yarn
npm install --save-dev schema-dts
yarn add --dev schema-dts
The examples below use schema-dts
with JSDoc @type
comments.
The Article schema defines an article from a news publication, a blog, or anything that publishes dated write-ups.
- TitletitleRich Text
- UIDuidUID
- AuthorauthorContent Relationship
- Publication Datepublication_dateDate
- Featured Imagefeatured_imageImage
Copy{
"id": "article",
"label": "Article",
"repeatable": true,
"status": true,
"json": {
"Main": {
"title": {
"type": "StructuredText",
"config": {
"label": "Title",
"placeholder": "",
"allowTargetBlank": true,
"single": "heading1"
}
},
"uid": {
"type": "UID",
"config": {
"label": "UID",
"placeholder": ""
}
},
"author": {
"type": "Link",
"config": {
"label": "Author",
"select": "document"
}
},
"publication_date": {
"type": "Date",
"config": {
"label": "Publication Date",
"placeholder": ""
}
},
"featured_image": {
"type": "Image",
"config": {
"label": "Featured Image",
"constraint": {},
"thumbnails": []
}
}
}
}
}
Article metadata can be queried and rendered like the following page. Note the use of next/head
to add the Schema.org metadata to the page’s <head>
.
import Head from 'next/head'
import * as prismic from '@prismicio/client'
import { createClient } from '../../prismicio'
/** @param {import("next").InferGetStaticPropsType<typeof getStaticProps>} */
export default function Page({ article }) {
/** @type {import('schema-dts').Article} */
const schema = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: prismic.asText(article.data.title),
author: {
'@type': 'Person',
name: prismic.asText(article.data.author.data.name),
// The full URL must be provided, including the website's domain.
url: new URL(
prismic.asLink(article.data.author),
'https://example.com'
),
},
image: prismic.asImageSrc(article.data.featured_image),
datePublished: article.data.publication_date,
dateModified: article.last_publication_date,
}
return (
<div>
<Head>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}
/** @param {import("next").GetStaticPropsContext<{ uid: string }>} */
export async function getStaticProps({ previewData, params }) {
const client = createClient({ previewData })
const article = await client.getByUID('article', params.uid, {
fetchLinks: ['author.name'],
})
return {
props: { article },
}
}
/** @type {import("next").GetStaticPaths} */
export async function getStaticPaths() {
const client = createClient()
const articles = await client.getAllByType('article')
return {
paths: articles.map((article) => prismic.asLink(article)),
fallback: false,
}
}
- NamenameRich Text
- UIDuidUID
- DescriptiondescriptionKey Text
- Start Datestart_dateTimestamp
- End Dateend_dateTimestamp
Copy{
"id": "event",
"label": "Event",
"repeatable": true,
"status": true,
"json": {
"Main": {
"name": {
"type": "StructuredText",
"config": {
"label": "Name",
"placeholder": "",
"allowTargetBlank": true,
"single": "heading1"
}
},
"uid": {
"type": "UID",
"config": {
"label": "UID",
"placeholder": ""
}
},
"description": {
"type": "Text",
"config": {
"label": "Description",
"placeholder": ""
}
},
"start_date": {
"type": "Timestamp",
"config": {
"label": "Start Date",
"placeholder": ""
}
},
"end_date": {
"type": "Timestamp",
"config": {
"label": "End Date",
"placeholder": ""
}
}
}
}
}
Event metadata can be queried and rendered like the following page. Note the use of next/head
to add the Schema.org metadata to the page’s <head>
.
import Head from 'next/head'
import * as prismic from '@prismicio/client'
import { createClient } from '../../prismicio'
/** @param {import("next").InferGetStaticPropsType<typeof getStaticProps>} */
export default function Page({ event }) {
/** @type {import('schema-dts').Event} */
const schema = {
'@context': 'https://schema.org',
'@type': 'Event',
name: prismic.asText(event.data.name),
description: event.data.description,
startDate: event.data.start_date,
endDate: event.data.end_date,
}
return (
<div>
<Head>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}
/** @param {import("next").GetStaticPropsContext<{ uid: string }>} */
export async function getStaticProps({ previewData, params }) {
const client = createClient({ previewData })
const event = await client.getByUID('event', params.uid)
return {
props: { event },
}
}
/** @type {import("next").GetStaticPaths} */
export async function getStaticPaths() {
const client = createClient()
const events = await client.getAllByType('event')
return {
paths: events.map((event) => prismic.asLink(event)),
fallback: false,
}
}
The FAQ schema defines a set of questions and answers typically shown on a Frequently Asked Questions page.
- UIDuidUID
- TitletitleTitle for the FAQsRich Text
- QuestionsquestionsGroup
- QuestionquestionA questionRich Text
- AnsweranswerThe answer to the questionRich Text
Copy{
"id": "faqs",
"label": "FAQs",
"repeatable": true,
"status": true,
"json": {
"Main": {
"uid": {
"type": "UID",
"config": {
"label": "UID",
"placeholder": ""
}
},
"title": {
"type": "StructuredText",
"config": {
"label": "Title",
"placeholder": "Title for the FAQs",
"allowTargetBlank": true,
"single": "heading1"
}
},
"questions": {
"type": "Group",
"config": {
"label": "Questions",
"fields": {
"question": {
"type": "StructuredText",
"config": {
"label": "Question",
"placeholder": "A question",
"allowTargetBlank": true,
"single": "heading1"
}
},
"answer": {
"type": "StructuredText",
"config": {
"label": "Answer",
"placeholder": "The answer to the question",
"allowTargetBlank": true,
"multi": "paragraph,preformatted,heading1,heading2,heading3,heading4,heading5,heading6,strong,em,hyperlink,image,embed,list-item,o-list-item,rtl"
}
}
}
}
}
}
}
}
FAQ metadata can be queried and rendered like the following page. Note the use of next/head
to add the Schema.org metadata to the page’s <head>
.
import Head from 'next/head'
import * as prismic from '@prismicio/client'
import { createClient } from '../../prismicio'
/** @param {import("next").InferGetStaticPropsType<typeof getStaticProps>} */
export default function Page({ faqs }) {
/** @type {import('schema-dts').FAQPage} */
const schema = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: faqs.data.questions.map((question) => ({
'@type': 'Question',
name: prismic.asText(question.question),
acceptedAnswer: prismic.asHTML(question.answer),
})),
}
return (
<div>
<Head>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}
/** @param {import("next").GetStaticPropsContext<{ uid: string }>} */
export async function getStaticProps({ previewData, params }) {
const client = createClient({ previewData })
const faqs = await client.getByUID('faqs', params.uid)
return {
props: { faqs },
}
}
/** @type {import("next").GetStaticPaths} */
export async function getStaticPaths() {
const client = createClient()
const faqs = await client.getAllByType('faqs')
return {
paths: faqs.map((faq) => prismic.asLink(faq)),
fallback: false,
}
}
See Google’s Search Central documentation for a collection of Schema.org guides.
Although Google’s examples are not Prismic-specific, the examples’ JSON should help you determine which Prismic fields are required.
Was this article helpful?
Can't find what you're looking for? Spot an error in the documentation? Get in touch with us on our Community Forum or using the feedback form above.