Sanity SEO done right: Open Graph, JSON-LD, fallbacks

Published: (May 19, 2026 at 04:25 PM EDT)
5 min read
Source: Dev.to

Source: Dev.to

SEO Baseline for Every Sanity Project

Every Sanity project we ship starts with the same SEO baseline:

  1. Top‑level title & description – used as the page heading/blurb and the default meta tags.
  2. SEO tab – overrides the top‑level fields when needed.
  3. Open Graph fields – sit one layer deeper for social‑card overrides.

All fields have sensible fallbacks, so a page always renders something even if an editor forgets to fill a field.

Why This Pattern Exists

Sanity can model anything – a blog, an e‑commerce catalogue, a live‑train‑times screen, etc.
Because the platform is deliberately unopinionated, it won’t pick an SEO pattern for you.
You bring the opinion; Sanity gives you the schema.

Field Layout

LayerFieldsPurpose
Top leveltitle, description, seoImage (fallback)Primary copy & default meta.
SEO tabseoTitle, seoDescription, seoImage, seoNoIndex, seoHideFromListsOverrides for search engines.
Open Graph (nested)ogTitle, ogDescription, ogImage, twitterTitle, twitterDescription, twitterImage, etc.Social‑card specific values.
  • Consistency – Editors write the page copy once (top level) and optionally override meta data in the SEO tab.
  • Fallbacks – Top‑level fields act as defaults, preventing empty renders.

seoHideFromLists

What does it do?
It’s a flag that lets editors keep a page indexable but exclude it from listing pages (e.g., blog index, product catalog).
Use it in your GROQ query to filter out hidden pages without setting noIndex.

Generating Structured Data

If the author, title, description, and publish date are already structured in Sanity, map them straight into JSON‑LD in your Next.js page:

import Head from 'next/head';
import { PortableText } from '@portabletext/react';

export default function Post({ data }) {
  const jsonLd = {
    "@context": "https://schema.org",
    "@type": "Article",
    headline: data.title,
    description: data.description,
    author: { "@type": "Person", name: data.author?.name },
    datePublished: data.publishDate,
    image: data.seoImage?.url,
  };

  return (
    <>
      {data.seoTitle || data.title}
      {JSON.stringify(jsonLd)}
      ## {data.title}
    </>
  );
}

Editors never touch the JSON‑LD; it stays in lockstep with the content.

How We Explain This (Video Script Outline)

“Hey bud, how you doing? I get asked this a lot, so I’m putting it on YouTube…”

  1. Sanity’s Unopinionated Nature – It can model anything, from a simple blog to a full e‑commerce system.
  2. Why We Need an SEO Opinion – The platform won’t decide for you; you must define a pattern.
  3. The TurboArt Implementation – Mirrors the official Sanity Learn docs but is streamlined for fast consumption.
  4. Baseline Fieldstitle, description, optional fallback seoImage.
  5. SEO Tab OverridesseoTitle, seoDescription, seoImage, seoNoIndex, seoHideFromLists.
  6. Open Graph Layer – Separate OG fields for Facebook, Twitter, LinkedIn, etc.
  7. Validation (Optional) – Character limits, required fields, etc., can be added if desired.

TL;DR

  • Top‑level title & description = default meta.
  • SEO tab = overrides (seoTitle, seoDescription, seoImage, seoNoIndex, seoHideFromLists).
  • Open Graph = deeper layer for social cards.
  • Fallbacks ensure nothing renders empty.
  • seoHideFromLists hides a page from internal listings while keeping it indexable.

Copy this pattern from Turbostart Sanity and you’ll have a solid, reusable SEO foundation for every project.

JSON‑LD & “Set‑and‑Forget” SEO

You can put JSON‑LD anywhere you like – in the “, in a component, etc.
A quick note on JSON‑LD: I much, much prefer a “set‑and‑forget” approach.

If you’re already entering titles, descriptions, authors, etc. in Sanity, let those fields automatically generate JSON‑LD.
That way you only have to fill in the data once, and the correct JSON‑LD appears without any extra work.

“You just enter the data inside of Sanity, and a like‑for‑like pair comes up with JSON‑LD. It isn’t that complicated these days with AI – you can even do it relatively quickly.”

Baseline SEO for Every Sanity Site

Use the following as a starting point for any Sanity‑based website (the exact setup may differ for other types of projects).

  1. Grab the Turbo‑Art Sanity starter from the Sanity Exchange or GitHub.

  2. Open the repository and focus on two key files:

    • query.js (or similar) – shows how we handle GROQ queries (Sanity’s query language).
    • seo.js (or seoFields.js) – contains the reusable SEO field definitions.

The SEO Fields We Use

FieldPurpose
seoTitleSEO‑friendly title
seoDescriptionMeta description
seoImageOpen‑Graph / Twitter image
seoNoIndexBoolean – tells search engines not to index the page
seoHideFromListsBoolean – removes the document from generated lists (e.g., blog indexes)

Why we need both seoNoIndex and seoHideFromLists

  • seoNoIndex – useful for pages you never want crawled (e.g., pay‑per‑click landing pages).
  • seoHideFromLists – useful when you want a page to stay searchable but not appear in internal listings (e.g., a “draft” blog post).

With seoHideFromLists you can write a GROQ filter like:

*[_type == "post" && !seoHideFromLists]{
  ...,
}

which returns all posts except those flagged to be hidden – a clean, maintainable solution.

Validation & Next Steps

  • The starter includes basic validation for each SEO field (required, max length, etc.).
  • If you run into any issues or need further guidance, just give us a shout – we’re happy to help and point you in the right direction.

Thanks for reading!
Have a great day.

Take care.

0 views
Back to Blog

Related posts

Read more »