Cover image for the article. Credits to Thought Catalog
Photo by Thought Catalogopens a new window

How I created my blog using Next.js and Sanity Part 1

8 minutes read
  • JavaScript

For a long time I wanted to create my own blog section where I could express my opinions on different subjects, share what I humbly know and keep acquiring more knowledge in the process.

So, know that I already have it, I thought it would be a cool idea to share the building process of this section, starting from what technologies I used to how I adapted it to satisfy my needs.

There are lots of different options for creating a blog. In my case, I chose the Next.js and Sanity duo. It's not a secret that I really enjoy working on Next.js, it's just such a great framework. Also, Sanity itself is a really powerful, cool and easy-to-use headless CMS. Well, enough talking. Let's dive into it now. In this article we'll be learning how to setup Sanity, create custom objects, schemas, and of course, how Sanity's rich text format works, which is the soul (and body) of this blog.

The steps

Installing Sanity CLI

First, we need to install Sanity's command line tool in order to create a new project by using the following npm command: npm install -g @sanity/cli

Creating a new Sanity project

Now that we already installed Sanity's CLI, we have to create our new project. Let's create a new folder called: sanity__backend

Then, go inside that folder and run the following command: sanity init

This will log you into Sanity, create a project, set up a dataset, and generate the files needed to run the development server locally.

If you are not logged in, there will be a prompt to do so. If you are, the sanity init command will prompt you for the following:

  1. Select an existing project or create a new project. (If you're in an existing project, you'll first be asked if you want to reconfigure it.
  2. Enter a project name. This will be the name used for the project at sanity.io/manage, where you can manage all your projects. If you selected an existing project in step 1, you will not see this option (but you can rename a project from the Settings tab of Manage).
  3. Select whether to use the default dataset configuration (a public dataset named production). If you select no, you will be prompted to enter a dataset name and visibility.
  4. Enter an output path. Since we already created a folder for our project to live in, we can type "./" in order to install all the dependencies on that same folder.
  5. Select a project template. Depending on your selection, your studio may be pre-configured with sample data and/or a sample schema from which you can model your content. In this particular case, we'll start with an empty project template.

Run the studio locally

Great! we already installed all our Sanity dependencies and we're ready to start writing our project. Let's start the development server for Sanity Studio with sanity start command and see how it looks like.

This builds the initial JavaScript code required to run the studio and starts a local web server. As you modify and save the code, the server will automatically rebuild the studio and refresh the browser.

The studio is just an HTML file and some JavaScript bundles that run in your browser. It talks to the Sanity API which stores your data and lets you query it from whatever platform or front-end you want.

Once you run the start command, this is what Sanity Studio will look like. Remember, we started with no predefined schemas, but you can also choose a pre-made Blog schema or any other option.
Once you run the start command, this is what Sanity Studio will look like. Remember, we started with no predefined schemas, but you can also choose a pre-made Blog schema or any other option.

Creating the blog schema

Cool, our development server is now working and running, let's populate it with our own blog schema.

Inside the sanity__backend folder, you will notice a folder called schemas, this is where we should create all our Studio's schemas and custom objects. Also, you will notice a schema.js file, this is where you will import and concatenate all your custom schemas and objects, but don't worry, we'll talk about this later.

Inside schemas folder, let's create a file called blog.js. In here we'll define our schema title, name, type and of course, the fields that it will contain. In my case those fields are: article title, slug, excerpt, publish date, cover image, article body (this is a quite interesting one, you'll see why soon) and social share image (sharing link card image).

import { MdNote } from "react-icons/md";

export default {
  title: "Blog",
  name: "blog",
  type: "document",
  icon: MdNote,
  fields: [
    {
      title: "Article Title",
      name: "articleTitle",
      type: "string",
    },
    {
      title: "Slug",
      name: "slug",
      type: "slug",
      options: {
        source: "articleTitle",
      },
    },
    {
      title: "Excerpt",
      name: "excerpt",
      type: "text",
    },
    {
      title: "Publish Date",
      name: "publishDate",
      type: "datetime",
    },
    {
      title: "Cover Image",
      name: "coverImage",
      type: "customImage",
    },
    {
      title: "Article Body",
      name: "articleBody",
      type: "richText",
    },
    {
      title: "Social Share Image",
      name: "socialShareImage",
      type: "image",
      options: {
        hotspot: true,
      },
    },
  ],
  preview: {
    select: {
      title: "articleTitle",
      image: "socialShareImage",
      publishDate: "publishDate",
    },
    prepare({ title, image, publishDate }) {
      return {
        title,
        media: image,
        subtitle: publishDate,
      };
    },
  },
};

Now, you've probably noticed some weird types in some of these fields. Sanity has its own predefined basic types such as: array, object, string, image, text, slug, etc. But you can also create your own types by using custom objects.

Creating custom image objects

Let's create 2 custom image objects that satisfy our needs, the process is quite similar to what we just saw, so nothing new here. The first one will be for our cover image, the second one will be for inserting images inside our article's body in the text editor that we are going to create very soon.

export default {
  title: "Custom Image",
  name: "customImage",
  type: "object",
  fields: [
    {
      title: "Image",
      name: "image",
      type: "image",
      options: {
        hotspot: true,
      },
    },
    {
      title: "Alternative Text",
      name: "altText",
      type: "string",
    },
  ],
};
import { MdImage } from "react-icons/md";

export default {
  title: "Image",
  name: "articleImage",
  type: "object",
  icon: MdImage,
  fields: [
    {
      title: "Image",
      name: "image",
      type: "image",
      options: {
        hotspot: true,
      },
    },
    {
      title: "Alternative Text",
      name: "altText",
      type: "string",
    },
    {
      title: "Caption Text",
      name: "caption",
      type: "string",
    },
  ],
};

Creating custom rich text object

This object will define our article's body structure according to what we need. Sanity's block type provides a rich text editor for block content with some default settings, but as you probably guessed, we can customize it.

Sanity has many schema types and those are outside of the scope of this article, to learn more about it check out Sanity's documentationopens a new window.

Since we want to have a technical blog, it's absolutely necessary to make sure that we can publish code blocks with proper syntax highlighting in our articles. Sanity provides a default code decorator, but it's nothing special. It doesn't provide syntax highlighting and other features that we are going to need. We need to install an additional plugin using the following command: sanity install @sanity/code-input

Now we will be able to use type: "code" in our code field and have a lot more features and proper syntax highlighting for a wide variety of languages. Before creating our rich text object, let's create a custom code object.

import { MdCode } from "react-icons/md";

export default {
  title: "Code",
  name: "customCode",
  type: "object",
  icon: MdCode,
  fields: [
    {
      title: "Code Filename",
      name: "codeFilename",
      type: "string",
      description: "example-code-filename",
    },
    {
      title: "Code",
      name: "code",
      type: "code",
    },
  ],
  preview: {
    select: {
      title: "code.code",
    },
  },
};

Cool, now let's work on the rich text editor.

export default {
  title: "Rich Text",
  name: "richText",
  type: "array",
  of: [
    {
      type: "block",
      title: "Block",
      styles: [
        { title: "H3", value: "h3" },
        { title: "H4", value: "h4" },
        { title: "Normal", value: "normal" },
        { title: "Block quote", value: "blockquote" },
      ],

      lists: [
        { title: "Bullet List", value: "bullet" },
        { title: "Numbered List", value: "number" },
      ],

      marks: {
        decorators: [
          { title: "Strong", value: "strong" },
          { title: "Emphasis", value: "em" },
          { title: "Inline Code", value: "code" },
        ],
        annotations: [
          {
            title: "URL",
            name: "link",
            type: "object",
            fields: [
              {
                title: "URL",
                name: "href",
                type: "url",
              },
              {
                title: "Is External Link?",
                name: "isExternal",
                type: "boolean",
                initialValue: false,
                description:
                  "This field should be true if the link points to an external URL.",
              },
            ],
          },
        ],
      },
    },
    {
      type: "articleImage",
    },
    {
      type: "customCode",
    },
  ],
};

We've added our own text styles, lists (bullet and numbered), marks (strong, emphasis and inline code for the decorators and links for the annotations) and also our custom article image and custom code objects.

Importing our schema and objects

In order to visualize these changes in our Studio, we need to import all our schemas and objects in the schema.js file:

// First, we must import the schema creator
import createSchema from "part:@sanity/base/schema-creator";

// Then import schema types from any plugins that might expose them
import schemaTypes from "all:part:@sanity/base/schema-type";

import blog from "./documents/blog";

//Custom objects
import customImage from "./objects/customImage";
import articleImage from "./objects/articleImage";
import customCode from "./objects/customCode";
import richText from "./objects/richText";

// Then we give our schema to the builder and provide the result to Sanity
export default createSchema({
  // We name our schema
  name: "default",
  // Then proceed to concatenate our document type
  // to the ones provided by any plugins that are installed
  types: schemaTypes.concat([
    /* Your types here! */
    blog,
    customImage,
    articleImage,
    customCode,
    richText,
  ]),
});

The result

Voilà! now we can see the changes reflected in our studio!

Our article title, slug, excerpt, publish date and cover image fields are here, great!
Our article title, slug, excerpt, publish date and cover image fields are here, great!
Also we can see our custom rich text editor with all our configurations and the social share image field.
Also we can see our custom rich text editor with all our configurations and the social share image field.

That's all for this article, hope you found it useful and/or entertaining. In the next article I will show you how to integrate all the work done today in your front-end. See you then!