Adding Sanity.io to a Next.js Site Part 2

Welcome back.

This post covers connecting the Sanity dataset we created in Part 1 to the frontend of our application.

Creating a Sanity Client Configuration

Let's start by adding a folder for Sanity-related files to our website repository. Mine is called /sanity and it lives in the /lib folder in the root. Within it, add a config.ts file. Although you could import the createClient() function in every page of your site that pulls in data from Sanity, we're going to abstract that process into a central location.

The file should contain the following contents:

import { createClient } from "next-sanity"

const config = {
    projectId: <your project id here>,
    dataset: 'production',
    apiVersion: "2023-02-23",
    useCdn: process.env.NODE_ENV === "production",
    // token: process.env.SANITY_TOKEN || ""
}

export const client = createClient(config)

Note that you'll need to add in your own projectId. You may eventually require a separate dataset; for now we're going to use the default one we created in the last post. apiVersion should be set to today's date in YYYY-MM-DD format. useCdn can be set to false or process.env.NODE_ENV === "production" - there's no need to distribute content to Sanity's CDN while in development.

Finally, consult the docs on how you'd like to handle access tokens - I won't be addressing that here.

Now that we've exported the client object, we can use it on a page.

Create a utility fetch function

The client object returned by createClient has a fetch method to send a query string to the Sanity Content Lake. We're going to combine it with some client-side error handling. This is the function we'll call in our /pages. In a file in /sanity called utils.ts (we'll be adding more utility functions later), add the following code:

import { client } from "./config";

export const fetchData = async (queryString: string, options = {}) => {
  try {
    const result = await client.fetch(queryString, options);
    if (!result) throw new Error("No result returned from query!");
    return result;
  } catch (err) {
    console.error(err);
  }
};

Create a GROQ query

GROQ is Sanity's proprietary query language and it's actually quite good. Another option is GraphQL but having used both I much prefer GROQ. Please have a look at the docs for in-depth syntax discussions.

In /sanity, add another folder called /queries and inside it a file called generalQueries.ts. Queries are strings and can build on each other and get quite complicated. For now we'll start with something light:

export const getAll = `
*[]
`

Now that we've got a query, we can create a new page in our /pages folder. Let's call it sanity-poc.tsx. It should look like this:

import { getAll } from "../lib/sanity/queries/generalQueries";
import { fetchData } from "../lib/sanity/utils";

export default function SanityPOC({ results }){
    return (
        <div>
            {JSON.stringify(results)}
        </div>
    )
}

export async function getStaticProps(){
    const results = await fetchData(getAll);

    return {
        props: { results }
    }
}

If we start up the Next.js dev server and load this page, we should see a load of JSON text, which, formatted looks something like this:

{
  "_createdAt": "2023-02-24T01:01:42Z",
  "_id": "5d1fa882-ab1e-4f3e-8340-af2046492b6a",
  "_rev": "InsINzKOPadxjwxEf7uXXy",
  "_type": "blogArticle",
  "_updatedAt": "2023-02-24T01:03:05Z",
  "body": [
    {
      "_key": "c46c5593dcba",
      "_type": "block",
      "children": [
        {
          "_key": "00410cce07f5",
          "_type": "span",
          "marks": [],
          "text": "This is body content. "
        }
      ],
      "markDefs": [],
      "style": "normal"
    }
  ],
  "description": "example description",
  "indexable": false,
  "publishedDate": "2023-02-24T01:01:20.577Z",
  "slug": {
    "_type": "slug",
    "current": "example-title"
  },
  "title": "Example Title"
}

What's going on here?

Let's break down what we did in the code above:

  1. Although we're running the dev server and this is all happening client-side, in a production environment Next would be running the getStaticProps() function on the server
  2. getStaticProps() is calling our utility fetchData() function and passing the query string getAll to the client.fetch() method exposed by the Sanity API
  3. We're passing the returned value as results to the props object, which gets passed to our SanityPOC() function, which then unmarshals the JSON and displays the data
  4. In a production environment, this data would all be sent to the client as static HTML

All non-media content stored in Sanity is serialized in this fashion and this is how you'll receive it on the frontend.

Before we wrap up, let's briefly look at our query getAll. This is a very basic query simply instructing Sanity to return absolutely everything for every document. In future queries we'll refine it - we certainly don't need all that data above.

At this point you should have the foundations laid out. Time to start playing around. Do a bit of research on GROQ and how you can manipulate your query. Build a refined version. If you just want to prototype GROQ queries, in the Sanity Studio on the top nav bar there is a Vision button that will let you play around with your own dataset in a sandboxed environment.

Next time, in Part 3, we'll add portableText, which allows us to map HTML tags to custom React components.