Search code examples
graphqltagsnext.jscategoriescontentful

Nextjs - Contentful - Tags - Dynamic pages


I would like to create dynamic pages when I click a tag in an article or elsewhere on my website.

I'm using Next.js, SSG, and fetching the articles containing the tags from Contentful with the following GraphQL queries:

export async function getArticles() {
  const articlesQuery = gql`
    {
      articleCollection(order: date_DESC) {
        items {
          title
          slug
          excerpt
          date
          contentfulMetadata {
            tags {
              name
              id
            }
          }
          featuredImage {
            title
            url
            width
            height
          }
          author {
            name
            photo {
              fileName
              url
              width
              height
            }
            title
            twitterProfile
            linkedInProfile
            slug
          }
        }
      }
    }
  `;
  return graphQLClient.request(articlesQuery);
}
export async function getArticle(slug) {
  const articleQuery = gql`
    query getArticle($slug: String!) {
      articleCollection(limit: 1, where: { slug: $slug }) {
        items {
          title
          slug
          excerpt
          date
          contentfulMetadata {
            tags {
              name
              id
            }
          }
          featuredImage {
            title
            url
            width
            height
          }
          author {
            name
            photo {
              fileName
              url
              width
              height
            }
            title
            twitterProfile
            linkedInProfile
            slug
          }
          content {
            json
            links {
              entries {
                block {
                  sys {
                    id
                  }
                  __typename
                  ... on VideoEmbed {
                    title
                    embedUrl
                  }
                  ... on CodeBlock {
                    description
                    language
                    code
                  }
                }
              }
              assets {
                block {
                  sys {
                    id
                  }
                  url
                  title
                  width
                  height
                }
              }
            }
          }
        }
      }
    }
  `;

  return graphQLClient.request(articleQuery, {
    slug,
  });
}

The contentfulMetadata is where the tags come from:

contentfulMetadata {
            tags {
              name
              id
            }
          }

This is my [id].jsx file:

import { getArticles, getArticle } from "@utils/contentful";

export async function getStaticPaths() {
  const data = await getArticles();

  return {
    paths: data.articleCollection.items.map((article) => ({
      params: { id: article.contentfulMetadata.tags[0].id },
    })),
    fallback: false,
  };
}

export async function getStaticProps(context) {
  const data = await getArticle(context.params.id);

  return {
    props: { article: data.articleCollection.items[0] },
  };
}

export default function TagPage({ article }) {
  return (
    <div>
      <h1>{article.contentfulMetadata.tags.id}</h1>
    </div>
  );
}

I get the following error: Error: Error serializing `.article` returned from `getStaticProps` in "/tags/[id]". Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.

When console.log(data.articleCollection.items.contentfulMetadata.tags.id); or console.log(data.articleCollection.items.contentfulMetadata.tags[0].id); within getStaticPaths function it provides the following error: TypeError: Cannot read property 'tags' of undefined

Can anyone show how to create a dynamic page ([id].jsx) file, which shows the tag id as the header <h1> as well as all articles containing the same tag?


Solution

  • Contentful DevRel here 👋🏼.

    article.contentfulMetadata.tags is an array, as an entry can have more than one tag. So you'll need to access the tag you want via article.contentfulMetadata.tags[0].id or article.contentfulMetadata.tags[desired_index].id and so on.

    Here's an example GraphQL query:

    query {
      blogPostCollection {
        items {
          contentfulMetadata {
            tags {
              id
              name
            }
          }
        }  
      }
    }
    

    And here's the response with tags as an array:

      "data": {
        "blogPostCollection": {
          "items": [
            {
              "contentfulMetadata": {
                "tags": [
                  {
                    "id": "salmastag",
                    "name": "Salma s tag"
                  }
                ]
              }
            },
            {
              "contentfulMetadata": {
                "tags": []
              }
            }
          ]
        }
      }
    }
    

    Notice how if a blog post doesn't have any PUBLIC tags assigned (the second entry in the response), an empty array is returned — you might want to do some safety checking in your code for this.