Trying to add a list of posts to my homepage instead of a separate page in Gatsby using Contentful

Viewed 130

I wish to place a list of posts on my home page instead of having to create a seperate dynamic page. This is my gatsby-node.js file

// DYNAMICALLY CREATE PAGES FOR EACH POST
module.exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions;
  const postTemplate = path.resolve('src/templates/news.js');
  const postResult = await graphql(`
    query {
      allContentfulPost {
        edges {
          node {
            slug
          }
        }
      }
    }
  `);

  // Handle errors
  if (postResult.errors) {
    reporter.panicOnBuild('Error while running GraphQL query.');
    return;
  }

  // Create the pages for each markdown file
  postResult.data.allContentfulPost.edges.forEach(({ node }) => {
    createPage({
      component: postTemplate,
      path: `/news/${node.slug}`,
      context: {
        slug: node.slug,
      },
    });
  });

  // PAGINATION FOR BLOG POSTS
  const postsResult = await graphql(`
    {
      allContentfulPost(sort: { fields: date, order: DESC }, limit: 1000) {
        edges {
          node {
            slug
          }
        }
      }
    }
  `);

  if (postsResult.errors) {
    reporter.panicOnBuild('Error while running GraphQL query.');
    return;
  }

  // Create blog-list pages
  const posts = postsResult.data.allContentfulPost.edges;
  const postsPerPage = 12;
  const postNumPages = Math.ceil(posts.length / postsPerPage);
  Array.from({ length: postNumPages }).forEach((_, i) => {
    createPage({
      path: i === 0 ? '/' : `/news/${i + 1}`,
      component: path.resolve('./src/templates/news-list.js'),
      context: {
        limit: postsPerPage,
        skip: i * postsPerPage,
        postNumPages,
        currentPage: i + 1,
      },
    });
  });
};

And this is my news-list.js file

import React from 'react';
import { Link, graphql } from 'gatsby';

import Layout from '../components/layout';
import SEO from '../components/seo';

export const query = graphql`
  query ($skip: Int!, $limit: Int!) {
    allContentfulPost(sort: { fields: date, order: DESC }, limit: $limit, skip: $skip) {
      edges {
        node {
          title
          slug
          date(formatString: "MMMM Do, YYYY")
        }
      }
    }
  }
`;

const NewList = (props) => {
  // const { postNumPages } = props.pageContext;

  const posts = props.data.allContentfulPost.edges;

  return (
    <Layout>
      <SEO title='News' />

      {posts.map(({ node }) => {
        const title = node.title || node.slug;
        return (
          <div className='container mx-auto prose prose-lg'>
            <div className='mb-2'>
              <Link to={`/posts/${node.slug}`}>
                <h3 className='underline font-sans mb-1'>{title}</h3>
              </Link>

              <div className='flex items-center justify-between'>
                <span className='font-mono text-sm'>{node.date}</span>
              </div>
            </div>
          </div>
        );
      })}
    </Layout>
  );
};

export default NewList;

I have tried to import the above news-list.js as component from my templates folder into my index.js folder. However I am getting the Error:

TypeError: Cannot read property 'allContentfulPost' of undefined

But if i add path: i === 0 ? '/news' : /news/${i + 1}, into my node file and go to localhost/news i get the list of posts.

But I want them on the home page.. So i thought If I was to just have / it would work turns out no.

How can i get the posts that are listed at LH/news to be displayed on my homepage instead.

Update

New Component after latest answer

import React from 'react';
import { useStaticQuery, graphql, Link } from 'gatsby';

import Layout from '../components/layout';

// import News from '../components/news';
// import NewsList from '../templates/news-list';

export const query = graphql`
  {
    allContentfulPost(sort: { fields: date, order: DESC }, limit: 1000) {
      edges {
        node {
          title
          slug
          date(formatString: "MMMM Do, YYYY")
        }
      }
    }
  }
`;

const Index = ({ data }) => {
  const { site } = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            companyname
          }
        }
      }
    `
  );

  return (
    <Layout>
      <section className='c-mt-10'>
        <div className=''>
          <div className='font-mono md:flex md:justify-between'>
            <div className='mb-5'>
              <a href={`mailto:hello@${site.siteMetadata.companyname}.co.uk`}>
                hello@pfb{site.siteMetadata.companyname}.co.uk
              </a>
              <br />
              <br />
              <tel>+44 020 3925 6054</tel>
            </div>

            <a
              href='https://www.google.com/maps/place/Warnford+Court,+29+Throgmorton+St,+London+EC2N+2AT/@51.5154096,-0.0890419,17z/data=!3m1!4b1!4m5!3m4!1s0x48761cacb440b98d:0x9742679143333ff!8m2!3d51.5154096!4d-0.0868479'
              target='_blank'
              rel='noreferrer'>
              <address className='text-right'>
                Warnford Court
                <br />
                29 Throgmorton Street
                <br /> London, EC2N 2AT
              </address>
            </a>
          </div>
        </div>

        <div>
          <h2>Company News</h2>
          <ul>
            {data.allContentfulPost.edges.map(({ node }) => (
              <li key={node.title}>
                <Link to={node.slug}>{node.title}</Link>
              </li>
            ))}
          </ul>
        </div>
      </section>
    </Layout>
  );
};
export default Index;
1 Answers

I think you are mixing a lot of concepts.

One thing is the gatsby-node.js queries, useful to create dynamic pages based on dynamic data (from Contentful CMS in your case) based on a parameter (slug in your case).

Another thing is page queries, a way of retrieving data in a top-level components (pages or templates, not components).

If you want to list all your post in your homepage, you just need to create a GraphQL query and loop through the results just like:

const IndexPage = ({ data }) => {

   return <Layout> 
    <ul>
    {data.allContentfulPost.edges.map(({node})=> <li key={node.title}><Link to={node.slug}>{title}</Link></li>)}
    </ul>
   </Layout>

}

export const query = graphql`
    {
      allContentfulPost(sort: { fields: date, order: DESC }, limit: 1000) {
        edges {
          node {
            title
            slug
            date(formatString: "MMMM Do, YYYY")
          }
        }
      }
    }
`;

When using page queries, your data is stored inside props.data so you can destructure them directly into data.

In your case, you were importing a template inside a page, which doesn't make much sense because you don't have, among other things, the query.

Related