Search code examples
reactjsnext.jscontentful

How to fetch category page data inside component in Next.js?


I would like to create a category page containing all tags added to articles. When clicking on a tag it should show a page with all articles containing that specific tag.

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

export async function getArticles() {
  const articlesQuery = gql`
    {
      articleCollection {
        items {
          title
          slug
          excerpt
          date
          contentfulMetadata {
            tags {
              name
              id
            }
          }
          featuredImage {
            title
            url
            width
            height
          }
        }
      }
    }
  `;
  return graphQLClient.request(articlesQuery);
}

The contentfulMetadata is where the tags come from:

contentfulMetadata {
    tags {
        name
        id
    }
}

I've then created a CategorySection component:

import styled from "styled-components";
import { getArticles } from "../../utils/contentful";
import Link from "next/link";

export async function getStaticProps() {
  const categories = await getArticles();

  return {
    props: {
      categories: categories.articleCollection.items,
    },
  };
}

export default function CategorySection({ categories }) {
  return (
    <Wrapper>
      <ContentWrapper>
        <CategoryWrapper>
          {categories.map((category) => {
            return (
              <Link href={`/articles/categories/${category.tags.name}`}>
                <Categories key={category.tags.id}>
                  {category.tags.name}
                </Categories>
              </Link>
            );
          })}
        </CategoryWrapper>
      </ContentWrapper>
    </Wrapper>
  );
}

The CategorySection component gives me the following error message:

TypeError: Cannot read property 'map' of undefined"

Below is my /pages/articles/categories/[slug].jsx file:

import styled from "styled-components";
import { getArticles, getArticle } from "../../utils/contentful";

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

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

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

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

export default function Category({ article }) {
  return <h1>{article.contentfulMetadata.tags.name}</h1>;
}

I'm getting the error below:

Error: A required parameter (slug) was not provided as a string in getStaticPaths for /articles/categories/[slug]

Can you help me understand how I create dynamic pages from my categories (tags)?


Solution

  • getStaticProps can only be used in page components, so in your case it'll be completely ignored in your CategorySection component. You'll need to fetch the data at the page level and pass it to the component where you want to use it.


    One possible solution is to simply pass the data as a prop down to the desired component.

    // pages/article
    
    import { getArticles } from "../../utils/contentful";
    
    export async function getStaticProps() {
        const categories = await getArticles();
    
        return {
            props: {
                categories: categories.articleCollection.items
            }
        };
    }
    
    export default function ArticlePage({ categories }) {
        return (
            <CategorySection categories={categories} />
        );
    }