DEV Community

Cover image for Server-Side Pagination in Next.js 13 Without Using 'Use Client'
Yasmin Sardar
Yasmin Sardar

Posted on

Server-Side Pagination in Next.js 13 Without Using 'Use Client'

If you want to get the benefits of using Next.js then you should the server-side rendering most of the time while creating your website and components such as Server Side Pagination using Next.js.

In many cases, using server-side rendering is not possible because of certain things we do, like using hooks to manage state, fetching data from servers, and adding events like onClick or onChange to elements.

The good thing is that in Next js you can create Server Side Pagination without using the state hook or any other elements which required client-side rendering.

Instead of using the useState hook to keep track of your page's current state, you can use a page query to store the current page value in your website's URL. This way, you can fetch data based on the user's current page, You can find all the steps and details about this process on this page.

Pagination Component for Next js Server Side Rendering

Before building the front-end first make sure your backend API Endpoint support Pagination for post, an simple example and Process of Build Pagination in Backend API Endpoint in Mentioned below in this post

Lets create some variables, Loops, and Functions, then I explain the HTML section.

Create Thee variables, "postLength" which store the total number of posts you have, I'm getting it's value from API but you can hard code it as well.

Second variable "perPage" which store the number of posts you want to show on your site.

let postLength = length; //Total post length [130]
let perPage = 20;  //Post per page
Enter fullscreen mode Exit fullscreen mode

Third will be the total of Pagination number "totalPage" to get the total Page value we need to divide "postLength" by "perPage".

let totalPage = Math.ceil(postLength / perPage);
Enter fullscreen mode Exit fullscreen mode

Now create a function that will loop through the page and return an Array of a total number of pages.

This function will return an Array of total number of pages need to show in the pagination.

let generatePaginationNumbers = (page, totalPage) => {
    let paginationArray = [];

    for (let i = + page - 2; i <= +page + 2; i++) {
      if (i < 1) continue;
      if (i > totalPage) break;
      paginationArray.push(i);
    }

    return paginationArray;
  };
Enter fullscreen mode Exit fullscreen mode

Call the function and provide the "page" and "totalPage" as a argument and store the function in a variable for easy access in the future.

let generatedNumbers = generatePaginationNumbers(page, totalPage);

// generatedNumbers = [1, 2, 3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

Now we can loop through "generatedNumbers" using the "map()" method to show the Pagication.

This function will return an Array of total number of pages need to show in the pagination.

let generatePaginationNumbers = (page, totalPage) => {
    let paginationArray = [];

    for (let i = + page - 2; i <= +page + 2; i++) {
      if (i < 1) continue;
      if (i > totalPage) break;
      paginationArray.push(i);
    }

    return paginationArray;
  };
Enter fullscreen mode Exit fullscreen mode

Call the function and provide the "page" and "totalPage" as a argument and store the function in a variable for easy access in the future.

let generatedNumbers = generatePaginationNumbers(page, totalPage);

// generatedNumbers = [1, 2, 3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

Now we can loop through "generatedNumbers" using the "map()" method to show the Pagication.

return (
    <div className="text-center">
      <div className="flex items justify-center gap-2">

//Map through generatedNumbers to get total numbers of pages available
        {generatedNumbers.map((number) => (
          <Link
            className={`${
              page == number
                ? "bg-blue-500 text-white"
                : "text-blue-500 bg-white"
            } inline-block py-1 px-3 rounded-lg border hover:border-blue-500`}
            href={`http://localhost:3000/?page=${number}`}
          >
            {number}
          </Link>
        ))}

      </div>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

To show a current page number as active you can check conditionally using turnory opraters,

If current page is available "generatedNumbers" Array then add a background color or other CSS property.

You can also add Next and Previous buttons in your pagination to help users to go to the next Page and Previous page.

If the page value is "0" or negative then hide the previous button,

Using the "Link" Conponent in Nextjs user can navigate previous page, the query value will be the current "page - 1"

//Previous Button

        {+page - 1 >= 1 && (
          <Link
            href={`http://localhost:3000/?page=${+page - 1}`}
            className="text-2xl text-blue-500 bg-white
            } inline-block gap-1 p-1 rounded-lg border"
          >
            <MdKeyboardArrowLeft />
          </Link>
        )}
Enter fullscreen mode Exit fullscreen mode

Similarly the next button will be the same but instead of negative one this time, the current query value will be the "page + 1".

We also want to hide the Next button once there is no page available to do so add a condition where if the page value is greater than "totalPage" then it will be hidden.

//Next Button

        {+page + 1 <= totalPage && (
          <Link
            href={`http://localhost:3000/?page=${+page + 1}`}
            className="text-2xl text-blue-500 bg-white
            } inline-block gap-1 p-1 rounded-lg border"
          >
            <MdKeyboardArrowRight />
          </Link>
        )}
Enter fullscreen mode Exit fullscreen mode

Next.js 13 Server-Side Pagination Component below

//Pagination Component
import Link from "next/link";
import { MdKeyboardArrowLeft, MdKeyboardArrowRight } from "react-icons/md";

const Pagination = ({ length, page }) => {
  let postLength = length;
  let perPage = 20;

  let totalPage = Math.ceil(postLength / perPage);

  let generatePaginationNumbers = (page, totalPage) => {
    let paginationArray = [];

    for (let i = +page - 2; i <= +page + 2; i++) {
      if (i < 1) continue;
      if (i > totalPage) break;
      paginationArray.push(i);
    }

    return paginationArray;
  };

  let generatedNumbers = generatePaginationNumbers(page, totalPage);

  return (
    <div className="text-center">
      <div className="flex items justify-center gap-2">
        {+page - 1 >= 1 && (
          <Link
            href={`http://localhost:3000/?page=${+page - 1}`}
            className="text-2xl text-blue-500 bg-white
            } inline-block gap-1 p-1 rounded-lg border"
          >
            <MdKeyboardArrowLeft />
          </Link>
        )}

        {generatedNumbers.map((number) => (
          <Link
            className={`${
              page == number
                ? "bg-blue-500 text-white"
                : "text-blue-500 bg-white"
            } inline-block py-1 px-3 rounded-lg border hover:border-blue-500`}
            href={`http://localhost:3000/?page=${number}`}
          >
            {number}
          </Link>
        ))}

        {+page + 1 <= totalPage && (
          <Link
            href={`http://localhost:3000/?page=${+page + 1}`}
            className="text-2xl text-blue-500 bg-white
            } inline-block gap-1 p-1 rounded-lg border"
          >
            <MdKeyboardArrowRight />
          </Link>
        )}
      </div>
    </div>
  );
};
export default Pagination;
Enter fullscreen mode Exit fullscreen mode

Now we have successfully created the Pagination component, but you need to follow one more steps before pagination, which is to add the component to our page pass the page and length props.

You may wondering from how I get page query, well in Next.js 13 you can get any query directly from page using searchParams prop.

import axios from "axios";
import Pagination from "./pagination";

const fetchPost = async (page) => {
  const res = await axios.get(
    `${process.env.NEXT_PUBLIC_POST}/pagination?page=${page ? page - 1 : 0}`
  );
  const data = await res.data;
  return data;
};

const fetchAllPost = async () => {
  const res = await axios.get(`${process.env.NEXT_PUBLIC_POST}`);
  const data = await res.data;
  return data;
};

//Getting "page" query sting from "searchParams" prop
const Page = async ({ searchParams: { page } }) => {
  const allPosts = await fetchAllPost();
  const posts = await fetchPost(page);

  return (
    <main>

//Pass length and Page props
      <Pagination length={allPosts.length} page={page ? page : 1} />
    </main>
  );
};
export default Posts;
Enter fullscreen mode Exit fullscreen mode

By following this method you will be able to add Pagination in your Next.js website in Server Side.

Setting up the API EndPoints in Server for Pagination

Before moving difrectly to the front-end first you need to make sure that your API end-points allows you to fetch a limited data as per your need,

For example this API "https://example.com/posts/?=1" only return 20 post and if we need more than I can use query to get another 20 post from the same API, such as "https://example.com/posts/?=2" or "https://example.com/posts/?=3" for another 20 post.

//API Endpoints for Pagination

"https://example.com/posts/?=1" = return 20 Posts

"https://example.com/posts/?=2" = skip 20 and return another 20 Posts

"https://example.com/posts"/?=3 = skip 30 and return another 20 Posts
Enter fullscreen mode Exit fullscreen mode

We need this functionallity so that all the post didn't get fetched at once.

So you created your backend by yourself using Node.js then make so to added this funtion, Below I mentioned how you can impleemnt the same function on your endpoint using few lines of code.

In your controller create a new endpoint to return all the posts from your database, in my case i'm using MongoDB.

//API for getting all the Posts

const getAllPost = async (req, res) => {
  try {
    const post = await postModel.find()

    res.status(200).json(post);
  } catch (error) {
    res.status(500).json(`Error in Getting All Posts, Error: ${error}`);
  }
};
Enter fullscreen mode Exit fullscreen mode

Now we need to add some extra methods to get only a few posts as required and we can do that easily by applying Mongoose methods.

before doing that, create a variable inside the getAllPost function and set how many posts you want to show,

const postPerPage = 20;   //20 Posts per Page
Enter fullscreen mode Exit fullscreen mode

Now we need to know on which page the use is you can use query to get the page value,

const page = req.query.page || 0;  //Get value from query, If no value available then set to "0"
Enter fullscreen mode Exit fullscreen mode

Once you create both the variable "postPerPage" and "page" now you need to use these values on the "skip" and "limit" methods.

const postPagination = async (req, res) => {
  const page = req.query.page || 0;
  const postPerPage = 20;

  try {
    const getPost = await postModel
      .find()
      .skip(page * postPerPage) //Here we're skipping Posts [1 * 20 = 20] as per Page & postPerPage value
      .limit(postPerPage); // Here we're limiting Posts per Page [20]
     .sort({ createdAt: -1 }); 
    res.status(200).json(getPost);
  } catch (error) {
    res.status(500).json(`Error in Getting Pagination Post, Error: ${error}`);
  }
};
Enter fullscreen mode Exit fullscreen mode

Now let's create a API Endpoint in the route file

router.get("/post/pagination", postPagination);  //End point for Pagination
Enter fullscreen mode Exit fullscreen mode

Congratulations, You just created an API Endpoint for Pagination.

This is one of the method of create a Server-Side Pagination using the latest Next.js 13, you can find so many other ways of create a Server-Side Pagination on the internet.

Let me know in the commend section if you need any help or suggection I would love to read them.

I hope this post helps you to create a Next js 13 Server-Side Pagination, you may also want to know https://newbiecoding.com/next-js-redirect-to-another-page/

Top comments (3)

Collapse
 
leandro_nnz profile image
Leandro Nuñez

Hi. Thanks for sharing but there are lots of misleading concepts.

  1. Client components are rendered in the server but hydrated in the client.
  2. It's not a good practice to use always server or always client components. Your project structure should be prepared for optimizing that "load". Next.js recommends using a mix of both. You should read the docs carefully.
  3. Next Link component allows you to use partial paths
  4. Next 13+ provides dynamic routing. You should use that for better performance.

Thanks for sharing

Collapse
 
yasminsardar profile image
Yasmin Sardar

Thanks for letting me know, but this method also works 👍

Collapse
 
leandro_nnz profile image
Leandro Nuñez

Yes, it works. So does COW programming language. It does not mean it’s useful nor accurate. The issues I pointed are facts. It’s a shame nowadays “writers” can’t take criticism. This is a very very bad practice.