DEV Community

Cover image for Fetching and Rendering Sanity Posts on your Homepage
Zachery Morgan
Zachery Morgan

Posted on • Edited on

Fetching and Rendering Sanity Posts on your Homepage

Like in my last lesson, there will be a reference image of my file structure and file content so you can compare.

Creating a Sanity client

We need to create a way for our app to fetch the data we store inside of Sanity. Luckily Sanity makes this very easy for us. You can read up more on what I'm using here. Sanity Client

npm i -g @sanity/client &&
npm i next-sanity
Enter fullscreen mode Exit fullscreen mode

This will install the Sanity client globally to use for any project.
We need to create a folder in our root named lib and a file within that folder named sanity.js and paste this code in your new file.

const sanityClient = require("@sanity/client");
const client = sanityClient({
  projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
  dataset: process.env.NEXT_PUBLIC_SANITY_DATASET,
  apiVersion: "2021-03-25", // use current UTC date - see "specifying API version"!
  useCdn: true, // `false` if you want to ensure fresh data
});

export default client;
Enter fullscreen mode Exit fullscreen mode

This code can be found on the Sanity Client package docs.


Fetching our posts

In your /pages/index.js file (not your /studio/schemas/index.js), at the bottom, you need to create an async/await function to fetch your data.

export async function getStaticProps() {}
Enter fullscreen mode Exit fullscreen mode

While at the top of this page you need to import the client we just created.

import client from "../lib/sanity";
Enter fullscreen mode Exit fullscreen mode

You can, and should, read up on getStaticProps here NextJS docs.
On that page it will explain what this function does, how it works, and when to use it. One of the reasons to use it is if The data comes from a headless CMS, which is exactly how we plan to use it.

Inside of our new function we are going to make a GROQ query to tell it what data to look for, how to order the data, and what pieces of that data we want returned to us.

export async function getStaticProps() {
  const query = `*[_type == "post"] | order(publishedAt desc) {
    _id,
    title,
    publishedAt,
    'slug': slug.current,
    body
  }`;

  const posts = await client.fetch(query);

  return {
    props: { posts },
  };
}
Enter fullscreen mode Exit fullscreen mode

I'm going to break each part of this function down to make it easier to understand, because just copying and pasting code without the knowledge of what each piece means isn't going to be very useful later on.

  • const query = `...`

    • Obviously this is just us declaring a variable for our query to use later. But what is important here is the fact that our code within the query is surrounded by back ticks.
  • *[_type == "post"]

    • This is telling our client to grab every entry we have in our database that has a name of post
    • You can see that we named our entries back when we created our 'Post' schema. That is what this "post" query is referring to. Post schema name example
  • | order(publishedAt desc)

    • Simple enough, this is telling our query to return the 'posts' that it fetched in an order by the published date in descending order. This means newest posts return first.
 {
    _id,
    title,
    publishedAt,
    'slug': slug.current,
    body
  }
Enter fullscreen mode Exit fullscreen mode
  • We are telling the fetch request what data we actually want returned. This is the power of GROQ, you only receive the data you want. Without this part you would get the entire 'post' data which looks something like this.
{
    "_createdAt": "2022-07-17T00:48:06Z",
    "_id": "f026b8eb-0fc6-4a58-8494-789def2703ff",
    "_rev": "IvZ71YmXkO22WtmwIxDKV0",
    "_type": "post",
    "_updatedAt": "2022-07-17T00:48:06Z",
    "body": (5) [{}, {}, {}, {}, {}],
    "publishedAt": "2022-07-17T00:45:31.070Z",
    "slug": {
        "_type": "slug",
        "current": "the-art-of-creating-a-post"
    },
    "title": "The art of creating a post"
}
Enter fullscreen mode Exit fullscreen mode

That's way more information than we need. So we are going to tell our query to only return to us the _id, title, publishedAt, slug, and body.
For the curious, slug is written as 'slug': slug.current because as you can see above, slug returns an object but we only need the current value, not the _type. So we are returning slug.current but assigning it to the slug key name.

  • const posts = await client.fetch(query);

    • Simple fetch request that uses the client we created and imported in this lesson, the query variable we just added, and assigns the return to a new variable named posts
  • return { props: { posts } };

    • Return props with our posts variable so our component can use it.

Rendering our posts

Back at the top of our /pages/index.js file we are currently in, we are going to add our posts variable we just fetched to our Home component.

export default function Home({ posts }) {
  return <h1>Hi :D</h1>;
}
Enter fullscreen mode Exit fullscreen mode

We are able to grabs our posts data from the props value by a method called destructuring.

Now to create HTML elements from our posts array. We will handle this by mapping over the data and saving it to a variable.

const postsElements = posts.map((post, index) => (
    <div key={index}>
      <p>{new Date(post.publishedAt).toDateString().slice(4)}</p>
      <h3>{post.title}</h3>
    </div>
  ));
Enter fullscreen mode Exit fullscreen mode

This will return a div with a p and h3 tag for each post we have.
We need the outer div simply because you need multiple elements to be wrapped in 1 so you can return it. Returning the p and the h3 without wrapping it in something will throw an error. We also add the key to this with the index of it's position in the array so React doesn't scream at us. (this is very useful to read up on, it gave me hours of debugging when I started learning React).

We add a p tag with the publishedAt value turned into a Date, turned into a string so HTML can read it, and we slice off the first 4 characters of the string (essentially removing the day of the week).

And finally an h3 with the title of our post.

Now if you add this variable in your return (wrapping it again in an outer div for the same reason as above, you must return 1 outer HTML element) like this.

return <div>{postsElements}</div>
Enter fullscreen mode Exit fullscreen mode

Closing your current npm run dev and starting it again you should see all of your posts (but only the title and published date) on your homepage by date they were published.

Posts rendered to homepage

It's quite ugly right now, but that's not the point. I will create a future lesson, or maybe edit this one, to add the methods I'm going to use to style it. I just want to get this out in the meantime to help people that need it.


Upcoming lessons

That's everything for this part. I might have went overboard explaining every little detail, but all of these concepts are very fundamental and will be reused later in this project (and most likely in your future projects). So I wanted to help you get a grasp of how everything works so you will have the knowledge to explore and make something yourself.

In the next part I will explain how to create dynamic urls for your posts so you can see the full information for it on it's on page.


References

- File Structure

Lesson 4 File Structure

- /pages/index.js

import client from "../lib/sanity";

export default function Home({ posts }) {
  const postsElements = posts.map((post, index) => (
    <div key={index}>
      <p>{new Date(post.publishedAt).toDateString().slice(4)}</p>
      <h3>{post.title}</h3>
    </div>
  ));
  return <div>{postsElements}</div>;
}

export async function getStaticProps() {
  const query = `*[_type == "post"] | order(publishedAt desc) {
    _id,
    title,
    publishedAt,
    'slug': slug.current,
    body
  }`;

  const posts = await client.fetch(query);

  return {
    props: { posts },
  };
}

Enter fullscreen mode Exit fullscreen mode

- /lib/sanity.js

const sanityClient = require("@sanity/client");
const client = sanityClient({
  projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
  dataset: process.env.NEXT_PUBLIC_SANITY_DATASET,
  apiVersion: "2021-03-25", // use current UTC date - see "specifying API version"!
  useCdn: true, // `false` if you want to ensure fresh data
});

export default client;
Enter fullscreen mode Exit fullscreen mode

Top comments (0)