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.

A diagram showing JSON for a Schema.org definition and its accompanying visual representation shown on search engine results. In this example, an event named "Successful Freelancer Shares Their Words of Wisdom" is represented with the Event schema.
Search engines use metadata to show rich search results, such as bookmarking an upcoming event.

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

General content modeling strategy

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.

JSDoc and TypeScript

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
Copy
npm install --save-dev schema-dts
Yarn
Copy
yarn add --dev schema-dts

The examples below use schema-dts with JSDoc @type comments.

Example: Article

The Article schema defines an article from a news publication, a blog, or anything that publishes dated write-ups.

Modeling article metadata

The following fields are needed to provide article metadata:

Articlearticle
Custom Type Model
Static Zone
  • Titletitle
    Rich Text
  • UIDuid
    UID
  • Authorauthor
    Content Relationship
  • Publication Datepublication_date
    Date
  • Featured Imagefeatured_image
    Image
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": []
        }
      }
    }
  }
}

Rendering article metadata

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>.

pages/articles/[uid].js
Copy
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) }}
        />
      </Head>
      <main>
        <p>
          The following schema has been added to the{' '}
          <code>&lt;head&gt;</code> of this page:
        </p>
        <pre>
          <code>{JSON.stringify(schema, null, 4)}</code>
        </pre>
      </main>
    </div>
  )
}

/** @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,
  }
}

Example: Event

The Event schema defines a date, time, location, and more for an event.

Modeling event metadata

The following fields are needed to provide event metadata:

Eventevent
Custom Type Model
Static Zone
  • Namename
    Rich Text
  • UIDuid
    UID
  • Descriptiondescription
    Key Text
  • Start Datestart_date
    Timestamp
  • End Dateend_date
    Timestamp
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": ""
        }
      }
    }
  }
}

Rendering event metadata

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>.

pages/event/[uid].js
Copy
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) }}
        />
      </Head>
      <main>
        <p>
          The following schema has been added to the{' '}
          <code>&lt;head&gt;</code> of this page:
        </p>
        <pre>
          <code>{JSON.stringify(schema, null, 4)}</code>
        </pre>
      </main>
    </div>
  )
}

/** @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,
  }
}

Example: FAQ

The FAQ schema defines a set of questions and answers typically shown on a Frequently Asked Questions page.

Modeling FAQ metadata

The following fields are needed to provide FAQ metadata:

FAQsfaqs
Custom Type Model
Static Zone
  • UIDuid
    UID
  • TitletitleTitle for the FAQs
    Rich Text
  • Questionsquestions
    Group
    • QuestionquestionA question
      Rich Text
    • AnsweranswerThe answer to the question
      Rich 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"
              }
            }
          }
        }
      }
    }
  }
}

Rendering FAQ metadata

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>.

pages/faqs/[uid].js
Copy
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) }}
        />
      </Head>
      <main>
        <p>
          The following schema has been added to the{' '}
          <code>&lt;head&gt;</code> of this page:
        </p>
        <pre>
          <code>{JSON.stringify(schema, null, 4)}</code>
        </pre>
      </main>
    </div>
  )
}

/** @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,
  }
}

More examples

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?
Not really
Yes, Thanks

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.