article

Tuesday, January 16, 2024

Next.js 14 Pagination MongoDB

Next.js 14 Pagination MongoDB

Install nextjs npx create-next-app@latest https://nextjs.org/docs/getting-started/installation

Install the following

npm install react-daisyui
https://www.npmjs.com/package/react-daisyui
daisyUI components built with React, Typescript and TailwindCSS

Mongoose
npm install mongoose
https://www.npmjs.com/package/mongoose

edit tailwind.config.ts Add daisyui to plugins
edit tailwind.config.ts
//edit tailwind.config.ts
import type { Config } from 'tailwindcss'

const config: Config = {
  content: [
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
    './app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {
      backgroundImage: {
        'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
        'gradient-conic':
          'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
      },
    },
  },
  plugins: [require('daisyui')], //https://www.npmjs.com/package/react-daisyui
}
export default config
.env
MONGODB_URI=mongodb://127.0.0.1/
app\page.tsx
//
import { dbConnect } from "@/lib/connectMongo";
import Link from "next/link";
import Image from 'next/image'

async function getData(perPage, page) {
    try {
        // DB Connect
        const client = await dbConnect();
        const db = client.db("nextjs14");

        // DB Query
        const items = await db
            .collection("products")
            .find({})
            .skip(perPage * (page - 1))
            .limit(perPage)
            .toArray();

        const itemCount = await db.collection("products").countDocuments({});

        const respnse = { items, itemCount };
        //console.log(items);
        console.log(itemCount);
        return respnse;
    } catch (error) {
        throw new Error("Failed to fetch data. Please try again later.");
    }
}

export default async function Page({ searchParams }) {
    let page = parseInt(searchParams.page, 10);
    page = !page || page < 1 ? 1 : page;
    const perPage = 4;
    const data = await getData(perPage, page);

    const totalPages = Math.ceil(data.itemCount / perPage);

    const prevPage = page - 1 > 0 ? page - 1 : 1;
    const nextPage = page + 1;
    const isPageOutOfRange = page > totalPages;

    const pageNumbers = [];
    const offsetNumber = 3;
    for (let i = page - offsetNumber; i <= page + offsetNumber; i++) {
        if (i >= 1 && i <= totalPages) {
            pageNumbers.push(i);
        }
    }

    return (
        <>
            <div className="container mx-auto">
                <div className="flex justify-between items-center">
                    <h1 className="font-bold py-10 text-2xl">Next.js 14 Pagination MongoDB</h1>
                </div>
                <table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
                    <thead className="bg-gray-100 dark:bg-gray-700">
                        <tr>
                            <th scope="col" className="p-4">
                                <div className="flex items-center">
                                    <input id="checkbox-all" type="checkbox" className="w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" />
                                    <label htmlFor="checkbox-all" className="sr-only">checkbox</label>
                                </div>
                            </th>
                            <th className="py-3 text-left">Image</th>
                            <th className="py-3 text-left">Product Name</th>
                            <th className="py-3 text-left">Price</th>
                            <th className="py-3 text-left">Category</th>
                            <th className="py-3 text-left">Actions</th>
                        </tr>
                    </thead>
                    <tbody className="bg-white divide-y divide-gray-200 dark:bg-gray-800 dark:divide-gray-700">
                        {data.items.map((item) => (
                            <tr key={item._id} className="hover:bg-gray-100 dark:hover:bg-gray-700">
                                    <td className="p-4 w-4">
                                        <div className="flex items-center">
                                            <input id="checkbox-table-1" type="checkbox" className="w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" />
                                            <label htmlFor="checkbox-table-1" className="sr-only">checkbox</label>
                                        </div>
                                    </td>
                                    <td>
                                        <Image
                                            src={item.image}
                                            alt={item.name}
                                            width={80}
                                            height={80}
                                            className="rounded-lg"
                                        />
                                    </td>
                                    <td>{item.name}</td>
                                    <td>${item.price}</td>
                                    <td>{item.category}</td>
                                    <td>
                                        Delete
                                    </td>
                                </tr>
                        ))}
                    </tbody>
                </table>

                {isPageOutOfRange ? (
                    <div>No more pages...</div>
                ) : (

                    <div className="flex justify-center items-center mt-16">

                        <div className="flex border-[1px] gap-4 rounded-[10px] border-light-green p-4">
                            {page === 1 ? (
                                <div className="opacity-60 py-2 px-5" aria-disabled="true">
                                    Previous
                                </div>
                            ) : (
                                <Link href={`?page=${prevPage}`} className="py-2 px-5" aria-label="Previous Page">
                                    Previous
                                </Link>
                            )}
                                
                            {pageNumbers.map((pageNumber, index) => (
                                <Link
                                    key={index}
                                    className={
                                        page === pageNumber
                                            ? "bg-blue-500 font-bold py-2 px-5 rounded text-white"
                                            : "bg-gray-500 hover:bg-gray-400 font-bold py-2 px-5 rounded text-white"
                                    }
                                    href={`?page=${pageNumber}`}
                                >
                                    {pageNumber}
                                </Link>
                            ))}

                            {page === totalPages ? (
                                <div className="opacity-60 py-2 px-5" aria-disabled="true">
                                    Next
                                </div>
                            ) : (
                                <Link href={`?page=${nextPage}`} className="py-2 px-5" aria-label="Next Page">
                                    Next
                                </Link>
                            )}
                        </div>
                    </div>

                )}

            </div>
        </>
    );
}
app\layout.tsx
//app\layout.tsx
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Next.js 14 MongoDB Pagination',
  description: 'Generated by create next app',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>{children}</body>
    </html>
  )
}
lib\connectMongo.ts
//lib\connectMongo.ts
import { MongoClient } from "mongodb";
const MONGODB_URI = process.env.MONGODB_URI;
let client = null;

export async function dbConnect() {
    if (client) {
        return client;
    }

    if (!MONGODB_URI) {
        console.log("MongoDb URI not found.");
    }

    try {
        client = await MongoClient.connect(MONGODB_URI);
        console.log("Connected to MongoDb successfully.");
        return client;
    } catch (error) {
        console.error("Error connecting to the database:", error);
    }
}
run C:\nextjs>npm run dev

Related Post