Getting Started

Welcome to the Sanity SEO Plugin Documentation. This guide will walk you through the process of installing and using the plugin, as well as explain the key SEO fields you can use to improve your website’s SEO.

The Sanity SEO Plugin helps you improve your website’s Search Engine Optimization (SEO) by adding SEO-related fields to your content in Sanity Studio. This makes your website more visible on Google and other search engines.

Installation

Let's get started with installing the plugin using npm,

1 npm install sanity-plugin-seo

Usage in Sanity Studio

Now, Open your sanity.config.ts or sanity.config.js file and add the below code snippet,

1 2 3 4 5 6 import { defineConfig } from "sanity"; import { seoMetaFields } from "sanity-plugin-seo"; export default defineConfig({ plugins: [seoMetaFields()], });

Now, add the SEO fields to your Sanity schema. This will include SEO fields into your pages within Sanity Studio.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const myDocument = { type: "page", name: "page", fields: [ { title: "Seo", name: "seo", type: "seoMetaFields", }, ], preview: { select: { metaTitle: "seo", }, prepare(selection) { const { metaTitle } = selection?.metaTitle || ""; return { title: metaTitle || "seo", }; }, }, };

Usage in Next.js

To keep your code clean and organized, it's a good practice to store your queries in separate files. It’s easier to manage and update them as needed. Let's assume you will create a new file named queries.ts inside your /lib/sanity/queries/demo.ts file.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 const groqQuery = groq`*[_type == "page"]{ _type, "slug":slug.current, ${seo}, }`; export const seo = /* groq */ `seo{ ${seofields} }`; export const seofields = /* groq */ ` _type, metaTitle, nofollowAttributes, seoKeywords, metaDescription, openGraph{ ${openGraphQuery} }, twitter{ ${twitterQuery} }, additionalMetaTags[]{ _type, metaAttributes[]{ ${metaAttributesQuery} } } `; export const twitterQuery = /* groq */ ` _type, site, creator, cardType, handle `; export const openGraphQuery = /* groq */ ` _type, siteName, url, description, title, image{ ${imageFields} } `; export const metaAttributesQuery = /* groq */ ` _type, attributeValueString, attributeType, attributeKey, attributeValueImage{ ${imageFields} } `; export const imageFields = /* groq */ ` _type, crop{ _type, right, top, left, bottom }, hotspot{ _type, x, y, height, width, }, asset->{...} `;

Now, You need to create types for the content fetched from Sanity in the file /lib/sanity/queries/demo.d.ts (TypeScript).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 export type SeoType = { _type?: "seo"; nofollowAttributes?: boolean metaDescription?: string; additionalMetaTags?: MetaTagType[]; metaTitle?: string; seoKeywords?: string[] openGraph?: OpenGraphType; twitter?: Twitter; }; export type MetaTagType = { _type: "metaTag"; metaAttributes?: MetaAttributeType[]; }; export type MetaAttributeType = { _type: "metaAttribute"; attributeKey?: string; attributeType?: string; attributeValueString?: string; attributeValueImage?: CustomImageType; }; export type OpenGraphType = { _type: "openGraph"; title: string; url?: string siteName?: string description: string; image: CustomImageType; }; export type Twitter = { _type: "twitter"; handle?: string; creator?: string site?: string; cardType?: string; }; export type CustomImageType = { _type: "customImage"; asset?: SanityImageAssetType; crop?: { _type: "SanityImageCrop"; right: number; top: number; left: number; bottom: number; }; hotspot?: { x: number; y: number; height: number; _type: "SanityImageHotspot"; width?: number; }; }; export type SanityImageAssetType = { _type?: "SanityImageAsset"; _id?: string; path?: string; url?: string; metadata?: { _type?: "SanityImageMetadata"; dimensions?: { _type?: "SanityImageDimensions"; height?: number; width?: number; }; }; };

Fetch and Pass SEO Metadata to CustomNextSeo in TypeScript or JavaScript,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 import React, { useMemo } from "react"; import type { PropsWithChildren } from "react"; import { NextSeo } from "next-seo"; import { MetaTag as NextSeoMetaTag, OpenGraph as NextSeoOpenGraph } from 'next-seo/lib/types'; import { CustomImageType, MetaAttributeType, MetaTagType, OpenGraphType, SeoType } from "../../../lib/sanity/types"; export const getOpenGraph = (args: OpenGraphType) => { const { description, image, title, _type, siteName, url } = args; const getImage = image ? resolveImage(image) : null; const values = { _type, description, siteName, url, title, images: [{ url: getImage ?? '' }], }; return values as NextSeoOpenGraph; } export const getMetaObjects = (tags: MetaTagType[], allowIndexing?: boolean) => { const tagArray: NextSeoMetaTag[] = []; tags.map(tag => { const excludeTag = !allowIndexing && !!tag.metaAttributes?.find( i => i?.attributeValueString?.includes('noindex') || i?.attributeValueString?.includes('nofollow'), ); if (!excludeTag) { const metaTag = getMetaAttribute(tag?.metaAttributes); if (metaTag) { tagArray.push(metaTag); } } }); return tagArray; } export const resolveImage = (image?: CustomImageType) => { return image?.asset?.url ?? ""; }; export const getMetaAttribute = (attrs: MetaAttributeType[] | undefined) => { if (!attrs) { return null; } const obj: Record<string, string> = {}; attrs.map((i) => { Object.assign(obj, { [i?.attributeKey as string]: i.attributeType == "image" ? resolveImage(i?.attributeValueImage) : i.attributeValueString, }); }); return obj as unknown as NextSeoMetaTag; } interface CustomNextSeoProps { seo: SeoType | null; slug: string; } const CustomNextSeo: React.FC<PropsWithChildren<CustomNextSeoProps>> = ({ seo, children, slug, }) => { const { additionalMetaTags, metaDescription, metaTitle, twitter, nofollowAttributes, seoKeywords } = seo || {}; const tags = useMemo( () => (additionalMetaTags ? getMetaObjects(additionalMetaTags) : []), [additionalMetaTags] ); const openGraph = useMemo( () => (seo?.openGraph ? getOpenGraph(seo?.openGraph) : undefined), [seo] ); const url = (process.env.NEXT_PUBLIC_APP_URL ?? "") + (slug?.startsWith("/") ? slug : `/${slug}`); return ( <> <NextSeo themeColor="" twitter={{ handle: twitter?.creator, site: twitter?.site, cardType: twitter?.cardType, }} nofollow={nofollowAttributes} noindex={nofollowAttributes} openGraph={openGraph} canonical={url || ""} additionalMetaTags={((seoKeywords && seoKeywords?.length > 0 ? [{ name: "keywords", content: seoKeywords?.join(", ") }] : []) as NextSeoMetaTag[]).concat(tags ?? [])} title={metaTitle ?? ""} description={metaDescription ?? ""} /> {children} </> ); }; export default CustomNextSeo;

The code creates a `CustomNextSeo` component to manage SEO settings for a Next.js page using the next-seo library. It takes care of things like meta descriptions, titles, keywords, and OpenGraph tags. The component accepts props like `seo` and `slug`, grabs SEO details, and generates the right metadata. This information is then passed to the `NextSeo` component, which renders it in the page’s `<head>`, helping improve SEO. It also handles custom Twitter tags and canonical URLs to make sure the page shows up well on social media and gets properly indexed by search engines.

Looking for a Reference ?

This documentation gives a proper guide to integrating Sanity CMS with a Next.js application, utilizing the new App Router for efficient routing. It covers both the frontend integration and the Sanity Studio setup to manage your content. The examples shared here will walk you through creating a powerful content-driven web application, giving you a streamlined experience for content management and frontend development.

# Frontend Demo with Next.js App Router

A modern frontend built with Next.js, by using the Next.js App Router.

Frontend demo with next App Router

# Frontend Demo

View a basic uses of the Next.js application that shows the integration of Sanity as the CMS.

Frontend demo

# Sanity Studio Demo

Take a look at the Sanity Studio, the content management system [CMS] where you can create, manage, and organize your content.

Studio Demo

Eager to Explore More?

For a more in-depth information, feel free to explore the blog linked below, which provides a comprehensive overview
of this plugin with sanity and nextjs.

Image

Step-by-Step Guide: Build an SEO-Friendly Website Using Sanity Plugin SEO