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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//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
1
MONGODB_URI=mongodb://127.0.0.1/
app\page.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//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