πŸ‘€ Our most exciting product launch yet πŸš€ Join us May 8th for Sanity Connect

Website Meta Document

By Andre Clark

Manage Meta tags, openGraph, and locale Data for your site within Sanity Studio

siteMeta.ts

import { openGraph } from "../objects";

export default {
  type: "document",
  name: "siteMeta",
  title: "Site Configuration",
  fieldsets: [
    { name: "google", title: "Google Analytics" },
  ],
  groups: [
    {
      name: "meta",
      title: "Site Info",
      default: true
    },
    {
      name: "og",
      title: "Social Share Info",
    },
    {
      name: "manifest",
      title: "Web App Settings",
      hidden: ({ document }: {  document: {
        [key: string]: never;
      }}): boolean => !(document.isPwa)
    },
    {
      name: "google",
      title: "Google Config",
      hidden: ({ document }: {  document: {
        [key: string]: never;
      }}): boolean => !(document.isGoogleAnalyticsEnabled)
    },
  ],
  fields: [
    {
    type: 'string',
    name: 'site_name',
    title: 'Site Name',
    group: ['og', 'meta'],
    // fieldset: "optional"
  },
  {
    type: "text",
    name: "ogDescription",
    title: "Social Share Description",
    group: ['og', 'meta']
  },
  {
    type: 'url',
    title: 'URL',
    name: 'url',
    description: 'Most likely either the url of the page or its canonical url',
    validation: (Rule: Rule) => Rule.required(),
    group: ['og', 'meta'],
    // fieldset: "basic"
  },
  {
    type: 'string',
    title: 'Page Title',
    name: 'ogTitle',
    description:
      'Set the title Open Graph should use. In most situations, this should be different from the value of the title prop',
    validation: (Rule: Rule) => Rule.required(),
    // fieldset: "basic"
  },
  {
    type: 'image',
    title: 'Image',
    name: 'ogImage',
    description:
      'URL of the image that should be used in social media previews. If you define this, you must define two other OG basic properties as well: title and type.',
    validation: (Rule: Rule) => Rule.required(),
    group: ['og'],
    // fieldset: "basic"
  },
    {
      type: "text",
      name: "description",
      title: "Describe This Site",
      group: ["meta", "og"]
    },
    {
      type: "boolean",
      name: "isPwa",
      title: "should this site be installable like an app?",
      group: [
        "meta", "manifest"
      ],
      initialValue: false,
      options: {
        layout: "checkbox"
      }
    },
    {
      type: "boolean",
      name: "isGoogleAnalyticsEnabled",
      title: "Enable Google Analytics?",
      group: ["meta", "google"],
      initialValue: false,
      options: {
        layout: "checkbox"
      }
    },
    {
      type: "string",
      name: "googleanalyticsId",
      title: "Google Analytics ID",
      fieldset: "google",
      group: ["meta", "google"],
    },
    {
      type: "string",
      name: "googleSiteVerificationId",
      title: "Google site Verification ID",
      fieldset: "google",
      group: ["meta", "google"],
    },
    {
      type: "manifest",
      title: "Web App Features",
      name: "manifest",
      group: "manifest"
    }
  ],
  initialValue: {
    isPwa: false,
    isGoogleAnalyticsEnabled: false,
  }
};

openGraph.ts

import type { Rule } from "@sanity/types";
import locale from "./locale";

export default {
  name: "openGraph",
  title: "Social Share Config",
  type: "object",
  fields: [
  {
    type: 'string',
    name: 'site_name',
    title: 'Site Name',
    group: ['og', 'meta'],
    // fieldset: "optional"
  },
  {
    type: "text",
    name: "ogDescription",
    title: "Social Share Description",
    group: ['og', 'meta']
  },
  {
    type: 'url',
    title: 'URL',
    name: 'url',
    description: 'Most likely either the url of the page or its canonical url',
    validation: (Rule: Rule) => Rule.required(),
    group: ['og', 'meta'],
    // fieldset: "basic"
  },
  {
    type: 'string',
    title: 'Page Title',
    name: 'ogTitle',
    description:
      'Set the title Open Graph should use. In most situations, this should be different from the value of the title prop',
    validation: (Rule: Rule) => Rule.required(),
    // fieldset: "basic"
  },
  {
    type: 'image',
    title: 'Image',
    name: 'ogImage',
    description:
      'URL of the image that should be used in social media previews. If you define this, you must define two other OG basic properties as well: title and type.',
    validation: (Rule: Rule) => Rule.required(),
    group: ['og'],
    // fieldset: "basic"
  },
  locale
]
}

locale.ts

const LOCALES: Locales = [
  {
    title: "American English",
    value: "en-US",
  },
  {
    title: "British English",
    value: "en-GB",
  },
  {
    title: "French",
    value: "fr-FR",
  },
];
const DISPLAYMODES: DisplayModes =  [
  'browser',
  'fullscreen',
  'minimal-ui',
  'standalone',
]
export default {
  type: "string",
  name: "locale",
  title: "Language",
  description:
    "used for informing the browser the site's language. Should be a valid bcp47 language code like en, 'en-US', 'no' or 'nb-NO'",
  options: {
    list: LOCALES
  },
  initialValue: LOCALES[0].value,
  group: ['meta', 'og'],
}

getSiteMetaData.ts

const querySiteMeta = `
*[_type=="siteMeta"][0] {
  title,
  description,
  "canonical": url,
  isGoogleAnalyticsEnabled,
  isPwa,
  manifest {
    ...,
    "background_color": background_color.hex,
    "theme_color": theme_color.hex
  },
  "openGraph": {
    "basic": { 
    title,
    url,
    "image": image.asset->url
    },
    "optional": {
      locale,
      site_name,
      description
    }
  }
}
`


export default async function getSiteMeta(
  query: string = querySiteMeta,
  client: SanityClientLike,
  mutation = "fetch"
):Promise<Site> {
  const site: Site = await client[mutation](query)
  return site
}

siteMeta.ts is ideally used as a singleton document within the settings/configuration section of your studio.

openGraph.ts and locale.ts can also be imported as individual objects/ fields to add anywhere into your schemas.

the siteMeta document can be fetched/queried with getSiteMeta.ts
the function also includes the groq query.

also available as a package

npm i sanity-meta

improvements and contributions welcome

github repository: https://github.com/AndreBClark/cosmic-schemas/tree/main/schemas/sanity-meta

Contributor