Blog thumbnail focusing on Next.js and Sanity SEO with a playful ghost cartoon character

SEO Magic with Next.js Pages Router and Sanity

  • Next.js
  • Sanity
  • SEO

Hey there! We all want our websites to hit the top of the search results. It’s not just about looking good; it's about getting seen. I’m here to walk you through a no-nonsense approach to upping your SEO game using Next.js Pages Router and Sanity.

Sanity + SEO = Smart Content Management

Sanity lets you neatly organize your content and SEO metadata. It’s like having a tidy workspace for your website's behind-the-scenes.

Setting Up Your SEO Fields in Sanity

Create an object for SEO in Sanity page's document schema. It looks something like this:

page.ts

1// Imports
2
3export default defineType({
4 name: 'page',
5 type: 'document',
6 fields: [
7 // Page fields, e.g. headline, slug, and modules
8 defineField({
9 title: 'SEO',
10 name: 'seo',
11 type: 'object',
12 fields: [
13 defineField({
14 name: 'title',
15 type: 'string',
16 }),
17 defineField({
18 name: 'description',
19 type: 'text',
20 }),
21 defineField({
22 title: 'OG Image',
23 name: 'ogImage',
24 type: 'image',
25 }),
26 ],
27 }),
28 ],
29});

Or if you have multiple documents that need SEO, you can create it as its own object and use it where you need to like this:

seo.ts

1import { defineField } from 'sanity';
2
3export default defineField({
4 title: 'SEO',
5 name: 'seo',
6 type: 'object',
7 fields: [
8 defineField({
9 name: 'title',
10 type: 'string',
11 }),
12 defineField({
13 name: 'description',
14 type: 'text',
15 }),
16 defineField({
17 title: 'OG Image',
18 name: 'ogImage',
19 type: 'image',
20 }),
21 ],
22});

And add it to your documents like this:

page.ts

1// Imports
2
3export default defineType({
4 name: 'page',
5 type: 'document',
6 fields: [
7 // Page fields, e.g. headline, slug, and modules
8 defineField({
9 title: 'SEO',
10 name: 'seo',
11 type: 'seo',
12 }),
13 ],
14});

Fill out the details in Sanity Studio, and you’re golden.

Creating a Reusable SEO Meta Component in Next.js

Every good SEO strategy begins with well-defined meta tags. With Next.js, this becomes easier to manage using the Head component from next/head.

Here's how you can create a simple reusable Meta component and begin with adding the title and description of the page:

meta.tsx

1import Head from 'next/head';
2
3interface Props {
4 seo: {
5 title: string;
6 description: string;
7 };
8};
9
10export default function Meta({ seo }: Props) {
11 return (
12 <Head>
13 <title>{seo.title} | Stelko</title>
14 <meta key="description" name="description" content={seo.description} />
15 </Head>
16 );
17};

You can read more about creating a reusable SEO Meta component at my other blog post SEO with Next.js' Pages Router.

Pulling SEO Data into Next.js

GROQ makes fetching this data a breeze, just add the SEO object to the end of your page queries:

queries.ts

1// Swap in your actual query parameters and page details.
2const query = groq`*[_type == "home"][0] {
3 ...,
4 seo
5}`;

You can fetch through a getStaticProps, if you are building out a static site. Or fetch on the client through a useEffect.

Fetching through getStaticProps

index.tsx

1// Imports
2
3export async function getStaticProps() {
4 // Fetch the home page and its contents
5 const home = await client.fetch(query);
6 // Return it as props
7 return { props: { home } };
8});
9
10export default function Home({ home }) {
11 return (
12 <>
13 {/* Show off your SEO with a reusable Meta component */}
14 <Meta seo={home.seo} />
15 {/* Page content */}
16 <>
17 );
18}

Fetching through useEffect

index.tsx

1// Imports
2
3export default function Home() {
4 // useState to store fetch results
5 const [home, setHome] = useState();
6 // useEffect to fetch results from Sanity
7 useEffect(() => {
8 // Fetch the home page and set its contents
9 client.fetch(query)
10 .then((res) => setHome(res));
11 }, []);
12 // Return page contents
13 return (
14 <>
15 {/* Show off your SEO with a reusable Meta component */}
16 <Meta seo={home.seo} />
17 {/* Page content */}
18 <>
19 );
20}

Fetching Dynamic Pages Data into Next.js

The process for fetching dynamic pages such as for those which are not your home page or your blog posts is exactly the same but this time we need we need to slug of the page or post.

Fetching for Static Pages

[slug].tsx

1// Imports
2
3// Statically generate all pages
4export async function getStaticPaths() {
5 const query = groq`*[_type == "page"].slug.current`;
6 const paths = getPageSlugs(query);
7 return {
8 // Return paths with / at the front
9 paths: [...(await paths).map((path) => `/${path}`)],
10 };
11});
12
13export async function getStaticProps({ params }: { params : { slug: string } }) {
14 const { slug } = params;
15 // Fetch the page and its contents
16 const page = await client.fetch(query, { slug });
17 // Return it as props
18 return { props: { page } };
19});
20
21export default function Page({ page }) {
22 return (
23 <>
24 {/* Show off your SEO with a reusable Meta component */}
25 <Meta seo={page.seo} />
26 {/* Page content */}
27 <>
28 );
29}

Fetching through the client

[slug].tsx

1// Imports
2
3import { useRouter } from 'next/router'
4
5export default function Page() {
6 const router = useRouter();
7 // useState to store fetch results
8 const [home, setPage] = useState();
9 // useEffect to fetch results from Sanity
10 useEffect(() => {
11 // Fetch the home page and set its contents
12 client.fetch(query, { slug: router.query.slug })
13 .then((res) => setHome(res));
14 }, []);
15 // Return page contents
16 return (
17 <>
18 {/* Show off your SEO with a reusable Meta component */}
19 <Meta seo={page.seo} />
20 {/* Page content */}
21 <>
22 );
23}

Conclusion: SEO Doesn't Have to Be Hard

And there you have it—SEO with Next.js and Sanity in a nutshell. No fluff, just the stuff you need to get your site ranking better.