Displaying WordPress posts with Next.js

6 July 2022

In the last post we retrieved all the published pages from our WordPress installation, created routes for each of them and inserted their content into a component to be displayed.

Now we’ll look at retrieving and displaying a list of posts from WordPress that link each item in the list to a page displaying the post. In our example we’ll use the default post type but the same process can be applied to any custom post type to retrieve an archive list and the posts.

Let’s create a folder for our blog posts to sit under the pages directory called blog. Within that we’ll create two JavaScript files – index.js and [slug].js

/pages
  /[slug].js
  /index.js

We’ll use the index.js file to curate a list of the title, excerpt and a link to the post. The [slug].js file will be used as a template to dynamically display the content of each post.

Creating index page

The index page will retrieve a list of all posts in WordPress using the getStaticProps function and then display the data returned. First off we need to create a function in our api.js file which will query WordPress for all our posts.

lib/api.js
export async function getAllPosts() {
  const data = await fetchAPI(`
    query AllPosts {
      posts(first: 1000, where: { orderby: { field: DATE, order: DESC}}) {
        edges {
          node {
            id
            date
            title
            slug
            excerpt
          }
        }
      }
    }
  `);
  return data?.posts;
}

This function utilises the fetchAPI function we previously built, and we’re passing in a GraphQL query which returns the first 1000 posts ordered from newest to oldest.

Back in our index.js file, this function we’ve just created is called within the getStaticProps function to retrieve all posts and pass them to the component in the props.

pages/index.js
import { getAllPosts } from '../../lib/api';

export async function getStaticProps() {
  const allPosts = await getAllPosts();
  return {
    props: {
      allPosts
    }
  }
}

We’ll then create the component which takes the allPosts prop, and we’ll extract the edges array from it. The edges array is looped through to get the slug, title and excerpt for each post.

pages/index.js
import { getAllPosts } from '../../lib/api';

export default function Blog({allPosts: { edges }}) {
  return (
    <>
      <h1>Blog</h1>
      {edges.map(({node}) =>(
        <section key="{node.id}">
          <link href="{`/blog/${node.slug}`}">
            <a>
              <h2>{node.title}</h2>
            </a>
          </link>
          <div dangerouslysetinnerhtml="{{" __html:="" node.excerpt="" }}></div>
        </section>
      ))}
    </>
  )
}

export async function getStaticProps() {
  const allPosts = await getAllPosts();
  return {
    props: {
      allPosts
    }
  }
}

Creating slug pages

Now that we have an index page set up, we need a component to display the content of each page. The process for this is very similar to what we used previously to display pages so we’ll go over it quickly.

The first thing we need to set up is a function in our API file which retrieves a specific post data from a slug. The function uses a GraphQL query which takes a slug variable to retrieve the post details.

lib/api.js
export async function getPost(slug) {
  const data = await fetchAPI(`
    fragment PostFields on Post {
      title
      excerpt
      slug
      date
      categories {
        nodes {
          name
          slug
         }
      }
    }
    query PostBySlug($id: ID!, $idType: PostIdType!) {
      post(id: $id, idType: $idType) {
        ...PostFields
        content
      }
    }
  `,
  {
    variables: {
      id: slug,
      idType: 'SLUG'
    }
  }
  );
  return data;
}

In our [slug].js file we use getStaticPaths to create the routes for each of the posts with the slug prepended with /blog/.

The getStaticProps function takes the params from the current page and passes them to the getPost function in api.js to retrieve data for a valid post. This post data is assigned to a prop which is used in the component to display the data from the GraphQL query.

pages/blog/[slug].js
import { getAllPostsWithSlug, getPost } from "../../lib/api";

export default function Post({postData}) {
  return (
    <>
      <h1>{ postData.title }</h1>
      <div dangerouslysetinnerhtml="{{__html:" postdata.content}}="">    
  )
}

export async function getStaticProps({params}) {
  const data = await getPost(params.slug);
  return {
    props: {
      postData: data.post
    }
  }
}

export async function getStaticPaths() {
  const allPosts = await getAllPostsWithSlug();
  return {
    paths: allPosts.edges.map(({ node }) => `/blog/${node.slug}`) || [],
    fallback: false
  };
}