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.
# Sanity Studio Demo
Take a look at the Sanity Studio, the content management system [CMS] where you can create, manage, and organize your content.
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.