DEV Community

Cover image for How to add a "load more" button to your blog
Caleb O.
Caleb O.

Posted on • Edited on

How to add a "load more" button to your blog

Last week, I added some features to my blog. From the preloader that utilized Next.js' router API, to the search and Table of Content component.

Recently, I've just added a minor feature that allows me to conditionally render a certain amount of articles on my blog. Instead of having all the articles I've written all at once on the page.

This feature is a bit common on some developer blogging platforms., and building it was a bit straightforward since I knew what needs to be done.

The idea behind it is for us to have an initial amount of articles listed whenever anyone navigates to the blog route. So, a variable like the one below should suffice

const initialPostList = 6
Enter fullscreen mode Exit fullscreen mode

And whenever a visitor clicks on the "load more" button, a callback function is triggered to increment the number of articles/posts on the blog.

It'll be ideal to also have another variable that holds the increment value. A variable like the one below should work fine.

const incrementInitialPostList = 4
Enter fullscreen mode Exit fullscreen mode

Watching the state

The snippets in the previous section are of great importance to this feature. initialPostList holds the amount of articles we want on initial render of the blog page, while incrementInitialPostList holds the value with which the articles list would be incremented.

With that said, some of the snippets here may look unfamiliar or intricate. If you happen to feel this way, when you're reading this, I'd implore you to check out this article that walks you through how to build a Next.js blog

I'm assuming you already have an idea of some Next.js data-fetching patterns, If not, please, take a look at the link I shared in the paragraph above.

I needed a way to keep these values — initialPostList and incrementInitialPostList — in the blog component's state, so that I can tap into its lifecycle methods .

The snippet below shows what the Blog component looks like.

import Article from './Article'; // Replace with your own Article component

const initialPostList = 6; // Number of articles to display initially
const incrementInitialPostList = 4; // Number of articles to add each time the "load more" button is clicked

export default function Blog() {
  const [displayPosts, setDisplayPosts] = React.useState(initialPostList);
  const [articles, setArticles] = React.useState(/* Your array of articles goes here */);

  const loadMore = () => {
    setDisplayPosts(displayPosts + incrementInitialPostList)
  }

  return (
    <div>
      {articles.slice(0, displayPosts).map(article => (
        <Article key={article.id} article={article} />
      ))}
      {displayPosts < articles.length ? ( 
          <button onClick={handleLoadMore}>Load More</button>
      ) : null}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Rendering more posts

In the snippet from the previous section, I'm using initialPostList variable to render the first 6 articles and adding the incrementInitialPostList variable to represent the number of articles to render each time the "load more" button is clicked.

The loadMore function simply updates the displayPosts state variable with the new value.

The articles array can come from any data source you like (e.g. an API endpoint, a static file, etc.), and you can customize the Article component to match the structure of your data.

But, in this case, it is coming from a markdown source, and I can get the list of articles with getStaticProps, a data-fetching method of Next.js

The blog component is modified like so;

export default function Blog({ posts }) {
  const [displayPosts, setDisplayPosts] = React.useState(initialPostList);
  const [articles, setArticles] = React.useState(posts);

  const loadMore = () => {
    setDisplayPosts(displayPosts + incrementInitialPostList)
  }

  return (
    <div>
      {articles.slice(0, displayPosts).map(article => (
        <Article key={article.id} article={article} />
      ))}
      {displayPosts < articles.length ? ( 
          <button onClick={loadMore}>Load More</button>
      ) : null}
    </div>
  );
}

export async function getStaticProps() {
  let articles = await getAllArticles()

  const sortedArticles = articles.map((article) => article)

  sortedArticles.sort((a, b) => {
    return new Date(b.publishedAt) - new Date(a.publishedAt)
  })

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

The slice function is used to only display the first item in the posts array through the value of the displayPosts value — which is 6 — from the articles array.

The displayPosts < articles.length condition is used to check if there are more articles to display and show the "load more" button accordingly.

From @nlxdodge's comment below, I found an approach that helps you preserve the state when you navigate to, and away from the page.

Take a look at it here

Top comments (2)

Collapse
 
nlxdodge profile image
NLxDoDge

Nice post, while I do like the Read More from a more simple perspective, also know that apps like Reddit also have this. But with infinite content the page/app becomes really slow.

So before implementing this feature, think about if this can impact peformance.
And does it also save the state? If you click on a link and click back. Does it reset and you have to click Read More again a couple of times to go back where you where? Then I would opt for the pagination approach instead.

Just my 2 cents as a Backend Developer 🙃

Collapse
 
seven profile image
Caleb O.

Oh! This is such a great input!!

I also thought of the infinite scroll feature, and I think this may be sort of related right? The pagination approach also sound superb!

About the state being in sync. I actually checked, and it isn't. Wow! this is such a nice observation. I'll look for a workaround.