Adding Sanity.io to a Next.js Site Part 1
For a relatively simple web log, you might be tempted to write blog posts in basic html, or even the truly lazy route: as one long <p> tag. While this is an easy approach, it might not be the best one for a number of reasons:
- it's bad for SEO
- you may be piling up technical debt if you decide to move away from this model in future
- you're missing an opportunity to learn a key skill as a developer
Besides, it's relatively easy to move to a CMS-based approach and the benefits are plentiful:
- it's easy to compose your text in a rich text editor, especially if it supports markdown
- the posts are framework-agnostic, so migration issues are mitigated
- collaboration is easy - anyone with access to the CMS portal can contribute to content generation
- by the same token, content can be managed from the CMS portal, not just within the codebase
- updates to the live site can be automated and don't require pull requests - this makes content generation easy for non-technical folks
- content releases can be scheduled
- CMSs can really improve SEO
- many, including the one we've selected, are free - at least to start
This post is will walk through a Sanity.io implementation in a Next.js website.
The basic steps are as follows:
- Sign up for a Sanity account
- Create a new Sanity project
- Gather your tools & dependencies
- Configure your studio and schema
- Create a config file
- Write a utility fetch function
- Design your queries
- Add portableText (optional)
That might seem like a lot, but it actually goes rather quickly. You could also just add one of the pre-built plugins/templates, of which Sanity provides many, but this post will focus on a from-scratch implementation.
Why Sanity?
I don't have a relationship with Sanity and this isn't a sponsored post.
There are many CMS platforms and I certainly haven't tried them all. Sanity's best feature is its flexibility. Because the infrastructure is all code, developers can much of it to suit their needs.
But Sanity can also be a frustrating experience for those that are using it for the first time. The docs are comprehensive, but aside from the very short introductory tutorial, there is a lack of chronological guidance. Once you finish the setup, you're kind of left to wonder, "OK, now what?" This, and following posts, hope to address some of that.
Let's get to it.
Sign up for a Sanity Account
Do this at sanity.io. There is a free tier to start you off. Choose that.
Create a new project
Before we create a new project, we need to discuss what that encompasses. The project folder will house all of the config files for this project's version of the Sanity Studio, the online portal where you will create content, be that blog posts, as in this article, or of any other type.
The project itself is not intrinsically tied to your website - you could pull content from this project into many websites - and it should not be located in the website's repository.
Start by opening a terminal and navigating to the location where the project folder will reside, then run npm -y create sanity@latest. If you use yarn as your project manager, don't worry, run the npm command anyway - you will have a chance to select the package manager of your choice for the project at a later step and running the npm command will not produce a package-lock.json file. You'll then go through a short setup process on the command line:
You may need to authorize your Sanity account or login.
If given the option, select "Create new project" and give your project a name.
Use the default dataset configuration with the name "production".
Confirm the output path.
Select "Clean project with no predefined schemas". We'll create those in a moment.
I'm using TypeScript, but the choice is yours.
Finally select your package manager. I'm using yarn - if you're using npm you'll need to modify any commands that follow.
If all went well, you'll see a 'Success!' message. Back in the sanity.io tab in the browser, refresh and you should see your new project in the navbar. Click on it and note the project ID.
Gather tools and dependencies
Next we're going to add the official Next.js Sanity plugin to our website repository root. We should also add the Sanity CLI tool - which can be added either to the Sanity project folder or globally.
Add to the root of your website:
yarn add next-sanity
Add to the or globally or globally
yarn add @sanity/cli
Configure the Studio and schema
You'll recall I mentioned that Sanity is very customizable - here we see it in action. Most CMS platforms would have you set up the schema in a GUI; with Sanity you do it in code.
Within the project folder, you'll see a schemas folder and within it an index.ts file. Let's create a new folder inside schemas which we'll call documents. Inside it, let's create a file called blogArticle.ts. This is where we define the parameters of a blog article in the form of a good old fashioned JS object.
export default {
title: 'Blog Article',
name: 'blogArticle',
type: 'document',
fields: [
{
title: 'Title',
name: 'title',
type: 'string',
},
{
title: 'Slug',
name: 'slug',
type: 'slug',
options: {
source: 'title',
auto: true,
},
},
{
title: 'Description',
name: 'description',
type: 'string',
},
{
title: 'Indexable',
name: 'indexable',
type: 'boolean',
description: 'toggle on/off to prevent being indexed on /blog',
initialValue: false,
},
{
title: 'Date Published',
name: 'publishedDate',
type: 'datetime',
options: {
auto: true
}
},
{
title: 'Body',
name: 'body',
type: 'array',
of: [
{type: 'block'},
{type: 'image'},
// {type: 'code'},
],
},
],
}
The exported object in blogArticle.ts has 4 properties: title, name, type and fields. In Sanity, title is how objects are displayed in the Studio GUI, while name is how they appear in code. type refers to Sanity's type system, which is not the same as that of TypeScript. The fields property is an array of objects representing the fields that each blog article should have.
Each blog article will have a title, a slug, a description, a published date, and a body. It will also have a boolean value called indexable - toggling this off will prevent it from being available in our queries - removing it from the blog without un-publishing it in Sanity.
You'll notice in the body object that it has a type of array with an of property - this specifies the various types that can be included in the body field. One entry, {type: code} is commented out. We'll come back to this when we build our codeblock component in a later post.
Once we've got the schema for our blog article complete, let's configure the Studio.
In the same folder (/schemas) open the index.ts file. Import the blogArticle schema and add it to the array of schema types like so:
import blogArticle from './documents/blogArticle'
export const schemaTypes = [blogArticle]
Next, let's head to sanity.config.ts in the project folder root. The config is pre-populated with all the information you need to run the Studio, but we'll update it later with an additional tool. For now, note that your projectId is written here in plain text. This isn't a secret value in and of itself - access is a separate issue dealt handled by access tokens and is outside the scope of this post - please check the Sanity docs to find the best fit for your website.
import {defineConfig} from 'sanity'
import {deskTool} from 'sanity/desk'
import {visionTool} from '@sanity/vision'
import {schemaTypes} from './schemas'
export default defineConfig({
name: 'default',
title: 'cs-blog',
projectId: <your project id here>,
dataset: 'production',
plugins: [deskTool(), visionTool()],
schema: {
types: schemaTypes,
},
})
That's it for the basic configuration - now let's see what we've accomplished. On the command line, in the project folder root, run yarn dev and open a browser tab to localhost:3333. You may be asked to authenticate - use the method you used when you created your Sanity account. You should then see your Studio.
Under the Content section on the left hand side, you should see one content type: Blog Article (remember, this corresponds to the title field of the of the schema we created in blogArticle.ts). Click Blog Article and try creating a new blog article. You should see a blank template with the fields we specified in our schema: Title, Slug, Description, Indexable, Date Published, and Body.
You'll notice that the options we added in the Slug object in the schema, source and auto, correspond to how the field is populated when you click the Generate button - the slug is derived from the Title field. Additionally, initialValue for Indexable is set to false. More on this later, but the general purpose is to provide ourselves with an additional check on whether an article can be returned by our query and published.
Add a bit of text to each of the fields, including the body, then hit Publish...and you're done! For now.
In Part 2, we'll write a blog post and import it into our Next.js frontend.