Making a nextjs & sanity website

Friday, 12 November 2021

post

Making my last portfolio with Nextjs and Sanity for one reason

I love making websites especially portfolio’s. This portfolio is the fifth iteration. So I have a good idea what content goes into a portfolio. A quick summery of what goes in: your projects, strengths, skills and education. Preferably connected with each other.

The solution for this is the JAM Stak. JAM stands for Javascript Api Markdown. For me the Javascript stands for Nextjs, The API part is Sanity and the Markdown is also markdown.
More information on Jamstack visit the community website.

Sanity as a headless CMS

With Sanity I can use their content lake. With the lake I can fetch, query and update with their hosted sanity studio.

To get started I created a my frontend of choice, Nextjs. So in the terminal I created the project and the sanity-client to connect with the frontend like so:

npx create-next-app my-project

cd my-project && sanity init

Now I have access to the sanity studio which is a CMS system with a REST API as an endpoint!

Sanity studio screenshot

The CMS is build on react and can be customized to your own preference. The sanity Studio is based on different schema’s that are also customizable through JSON objects And is structured like this:

export default {
  name: "posts",
  title: "Blog Post",
  type: "document",
  fields: [
    {
      name: "title",
      title: "Title",
      type: "string",
    },
    {
      name: "slug",
      title: "Slug",
      type: "slug",
      options: {
        source: "title",
        maxLength: 96,
      },
    },
    {
      name: "description",
      title: "Description",
      type: "string",
      validation: (Rule) => Rule.max(120),
    },
    {
      name: "author",
      title: "Author",
      type: "reference",
      to: { type: "contributors" },
    },
    {
      name: "mainImage",
      title: "Main image",
      type: "image",
      options: {
        hotspot: true,
      },
    },
    {
      name: "categories_tech",
      title: "Categories Technique",
      type: "array",
      of: [{ type: "reference", to: { type: "skills" } }],
    },
    {
      name: "body",
      title: "Body",
      type: "array",
      of: [
        {
          type: "block",
          // Only allow these block styles
          styles: [
            { title: "Normal", value: "normal" },
            { title: "H1", value: "h1" },
            { title: "H2", value: "h2" },
            { title: "Quote", value: "blockquote" },
          ],
          // Only allow numbered lists
          lists: [
            { title: "Bullet", value: "bullet" },
            { title: "Numbered", value: "number" },
          ],

        },
        {
          title: "Code Snippet",
          name: "code_snippet",
          type: "object",
          fields: [
            {
              title: "Code",
              name: "code",
              type: "code",
            },
          ],
        },
        {
          title: "Screenshot",
          name: "screenshot",
          type: "object",
          fields: [
            {
              name: "image",
              title: "Image",
              type: "image",
              options: {
                hotspot: true // <-- Defaults to false
              },
            }
          ],
        },
      ],
    },
  ],

  preview: {
    select: {
      title: "title",
      author: "author.name",
      media: "mainImage",
    },
    prepare(selection) {
      const { author } = selection;
      return Object.assign({}, selection, {
        subtitle: author && `by ${author}`,
      });
    },
  },
};


It is possible to query through the content with sanity’s own query language named GROQ but it’s also possible to use GraphQL. Sanity has fully documented what is possible with the query language. With nextjs I can query through the sanity client like this:

*[_type == "skills"] | order(start_date desc) | order(categories->title desc) {
    _id,
    skill_name,
    start_date,
    "type": categories->title
  }

Display content in Nextjs frontend

Now that I’ve set up my sanity studio I can now connect the endpoint of sanity with the frontend of my nextjs app. To do that I’ve installed the following dependencies:

{
  "dependencies": {
    "@sanity/client": "^2.19.0",
    "next": "^12.0.1",
    "next-pwa": "^5.3.1",
    "next-sanity": "^0.4.0",
    "react": "17.0.2",
    "react-dom": "17.0.2",
  }
}

Then I created the following file in a lib folder. In there I'm importing next-sanity to use some of their methods for the frontend.

import {
  createClient,
  createImageUrlBuilder,
  createPreviewSubscriptionHook,
  createPortableTextComponent,
} from "next-sanity";

const config = {
  projectId: process.env.SANITY_PROJECT_ID,
  dataset: process.env.SANITY_DATASET,
  apiVersion: "2021-09-28",
  useCdn: true,
};

export const sanityClient = createClient(config);
export const usePreviewSubscription = createPreviewSubscriptionHook(config);
/**
 * Set up a helper function for generating Image URLs with only the asset reference data in your documents.
 * Read more: https://www.sanity.io/docs/image-url
 **/
export const urlFor = (source) => createImageUrlBuilder(config).image(source);

// Set up the client for fetching data in the getProps page functions
export const PortableText = createPortableTextComponent({
  ...config,
  serializers: {},
});

Now I can use this file to configure, query and display my content from sanity. Nextjs allows my to generate html files incrementally called ISR ( incremental static regeneration ). To get the content from the sanity client I use getstaticprops a method in nextjs like so:

import {
  getContributors,
  getProjects,
  getSkills,
} from "../hooks/get-sanity-data";
export const getStaticProps = async () => {
  let result = {};

  // fetch content
  const profile = await getContributors();
  const projects = await getProjects();
  const skills = await getSkills();

  // set api objects in result object
  result.profile = profile;
  result.projects = projects;
  result.skills = skills;

  return {
    props: result,
    revalidate: 10, // In seconds
  };
};
export default Index;

Nextjs passes the props to the JSX elements as a object. As a developer I can view the object in the sanity studio shown below:

query sanity content within sanity studio

Like I said earlier sanity client allows me to query content and display them through props in nextjs. With these tools I’ve created the project page component like this:

const ProjectsPage = ({ projects, skills, profile }) => {
  return (
    <>
      <Head>
        <title>{titleName} — Projects</title>
      </Head>
      <Content status={profile[0]}>
        <Name content={profile[0]} />
        <PageIndicator name={projects[0]?._type} left={false} />
        <MainCard>
          <ProjectsGrid content={projects} shownItems={projects.length} />
          <SkillsScroll content={skills} header="My tech stack" />
        </MainCard>
      </Content>
    </>
  );
};
import {
  getProjects,
  getSkills,
  getContributors,
} from "../hooks/get-sanity-data";
export const getStaticProps = async () => {
  let result = {};
  const profile = await getContributors();
  const projects = await getProjects();
  const skills = await getSkills();
  // const example = await sanityClient.fetch(`*[_type == "case_studies"]`);
  // result.example

  result.projects = projects;
  result.skills = skills;
  result.profile = profile;

  return {
    props: result,
    revalidate: 10, // In seconds
  };
};

Benefits

In general these are the points why I choose to use the JAM Stack.
First of all the CMS system of sanity is easy to use for the users and developers. I am passionated about frontend development so I wanted to use one like nextjs. So in short the benefits for me are:

  • Sanity content lake and client dependency
  • nextjs framework
  • Sanity studio with a REST API

Conclusion

Using frameworks like these are amazing! I hope that everyone is seeing how beneficial headless CMS's are for the hole team / organization. The sanity studio helps managing the content in an easy way. Nextjs is great for dynamic data such as a blog. The options for SSR, SSG and ISR. choosing the latter was a good option. The end result is for me perfect.

Related projects


Nextjs Portfolio.

Website / Web App
September 2021
Next logo
Tailwindcss logo
Netlify logo
GitHub logo
Sanity logo

Updated portfolio website that uses API connected to Next.

nextjs-portfolio