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.js Add daisyui to plugins
edit tailwind.config.js
//edit tailwind.config.js 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/nextjs14app\layout.js
//app\layout.js import { Inter } from 'next/font/google' import './globals.css' import Navbar from "@/components/Navbar"; const inter = Inter({ subsets: ['latin'] }) export const metadata = { title: 'Create Next App', description: 'Generated by create next app', } export default function RootLayout({ children }) { return ( <html lang="en"> <body className={inter.className}> <div className="container mx-auto px-4"> <Navbar /> <div className="pb-8">{children}</div> </div> </body> </html> ) }components\Navbar.jsx
//components\Navbar.jsx import Link from "next/link"; export default function Navbar() { return ( <div className="navbar bg-base-100"> <div className="flex-1"> <a className="btn btn-ghost text-xl">Cairocoders</a> </div> <div className="flex-none"> <div className="dropdown dropdown-end"> <div tabIndex={0} role="button" className="btn btn-ghost btn-circle"> <div className="indicator"> <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" /></svg> <span className="badge badge-sm indicator-item">8</span> </div> </div> <div tabIndex={0} className="mt-3 z-[1] card card-compact dropdown-content w-52 bg-base-100 shadow"> <div className="card-body"> <span className="font-bold text-lg">8 Items</span> <span className="text-info">Subtotal: $999</span> <div className="card-actions"> <button className="btn btn-primary btn-block">View cart</button> </div> </div> </div> </div> <div className="dropdown dropdown-end"> <div tabIndex={0} role="button" className="btn btn-ghost btn-circle avatar"> <div className="w-10 rounded-full"> <img alt="Tailwind CSS Navbar component" src="https://daisyui.com/images/stock/photo-1534528741775-53994a69daeb.jpg" /> </div> </div> <ul tabIndex={0} className="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52"> <li> <a className="justify-between"> Profile <span className="badge">New</span> </a> </li> <li><a>Settings</a></li> <li><a>Logout</a></li> </ul> </div> </div> </div> ); }.env
MONGODB_URI=mongodb://127.0.0.1/nextjs14components\EditProductForm.jsx
//components\EditProductForm.jsx "use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; export default function EditProductForm({ id, name, image, price,category }) { const [newName, setNewTitle] = useState(name); const [newImage, setNewImage] = useState(image); const [newPrice, setNewPrice] = useState(price); const [newCategory, setNewCategory] = useState(category); const router = useRouter(); const handleSubmit = async (e) => { e.preventDefault(); try { const res = await fetch(`http://localhost:3000/api/products/${id}`, { method: "PUT", headers: { "Content-type": "application/json", }, body: JSON.stringify({ newName, newImage, newPrice, newCategory }), }); if (!res.ok) { throw new Error("Failed to update Product"); } router.refresh(); router.push("/products"); } catch (error) { console.log(error); } }; return ( <> <div className="flex justify-between items-center"> <h1 className="font-bold py-10 text-2xl">Update Product</h1> </div> <form onSubmit={handleSubmit} className="flex flex-col gap-3"> <input onChange={(e) => setNewTitle(e.target.value)} value={newName} className="input input-bordered input-accent w-full max-w-xs" type="text" /> <input onChange={(e) => setNewImage(e.target.value)} value={newImage} className="input input-bordered input-accent w-full max-w-xs" type="text" /> <input onChange={(e) => setNewPrice(e.target.value)} value={newPrice} className="input input-bordered input-accent w-full max-w-xs" type="text" /> <input onChange={(e) => setNewCategory(e.target.value)} value={newCategory} className="input input-bordered input-accent w-full max-w-xs" type="text" /> <button className="btn btn-primary w-full max-w-xs"> Update Product </button> </form> </> ); }components\ProductsList.jsx
//components\EditProductForm.jsx import Link from "next/link"; import RemoveBtn from "./RemoveBtn"; import Image from 'next/image' const getProducts = async () => { try { const res = await fetch("http://localhost:3000/api/products", { cache: "no-store", }); if (!res.ok) { throw new Error("Failed to fetch products"); } return res.json(); } catch (error) { console.log("Error loading products: ", error); } }; export default async function ProductssList() { const { products } = await getProducts(); return ( <> <div className="overflow-x-auto"> <div className="flex justify-between items-center"> <h1 className="font-bold py-10 text-2xl">Next.js 14 CRUD Crate, Read, Update and Delete - MongoDB Daisyui TailwindCSS</h1> </div> <div className="text-right"> <Link className="btn btn-primary" href={"/addProduct"}> Add Product </Link> </div> <table className="table"> <thead> <tr> <th> <label> <input type="checkbox" className="checkbox" /> </label> </th> <th>Name</th> <th>Price</th> <th>Category</th> <th /> </tr> </thead> <tbody> {products.map((rs) => ( <tr className="hover" key={rs._id}> <th> <label> <input type="checkbox" className="checkbox" /> </label> </th> <td> <div className="flex items-center gap-3"> <div className="avatar"> <div className="mask mask-squircle w-12 h-12"> <Image src={rs.image} alt={rs.name} width={80} height={80} className="rounded-lg" /> </div> </div> <div> <div className="font-bold">{rs.name}</div> </div> </div> </td> <td> ${rs.price} </td> <td>{rs.category}</td> <th> <Link href={`/editProduct/${rs._id}`}> <button className="btn btn-primary">Edit</button> </Link> <RemoveBtn id={rs._id} /> </th> </tr> ))} </tbody> </table> </div> </> ); }components\RemoveBtn.jsx
//components\RemoveBtn.jsx "use client"; import { useRouter } from "next/navigation"; export default function RemoveBtn({ id }) { const router = useRouter(); const removeProduct= async () => { const confirmed = confirm("Are you sure?"); if (confirmed) { const res = await fetch(`http://localhost:3000/api/products?id=${id}`, { method: "DELETE", }); if (res.ok) { router.refresh(); } } }; return ( <button onClick={removeProduct} className="btn btn-error ml-2"> Delete </button> ); }libs\mongodb.js
//libs\mongodb.js import mongoose from "mongoose"; const connectMongoDB = async () => { try { await mongoose.connect(process.env.MONGODB_URI); console.log("Connected to MongoDB."); } catch (error) { console.log(error); } }; export default connectMongoDB;models\ProductModel.js
//models\ProductModel.js import mongoose, { Schema } from "mongoose"; const topicSchema = new Schema( { name: { type: String, required: true }, category: { type: String, required: true }, image: { type: String, required: true }, price: { type: Number, required: true }, }, { timestamps: true, } ); const ProductModel = mongoose.models.Product || mongoose.model("Product", topicSchema); export default ProductModel;app\products\page.js
//app\products\page.js import Productlist from "@/components/ProductsList"; export default function Home() { returnapp\addProduct\page.jsx; }
//app\addProduct\page.jsx "use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; export default function AddProduct() { const [name, setName] = useState(""); const [image, setImage] = useState(""); const [price, setPrice] = useState(""); const [category, setCategory] = useState(""); const router = useRouter(); const handleSubmit = async (e) => { e.preventDefault(); if (!name || !image) { alert("Name and image are required."); return; } try { const res = await fetch("http://localhost:3000/api/products", { method: "POST", headers: { "Content-type": "application/json", }, body: JSON.stringify({ name, image, price, category }), }); if (res.ok) { router.push("/products"); } else { throw new Error("Failed to create a Product"); } } catch (error) { console.log(error); } }; return ( <> <div className="flex justify-between items-center"> <h1 className="font-bold py-10 text-2xl">Add New Product</h1> </div> <form onSubmit={handleSubmit} className="flex flex-col gap-3"> <input onChange={(e) => setName(e.target.value)} value={name} className="input input-bordered input-accent w-full max-w-xs" type="text" placeholder="Product Name" /> <input onChange={(e) => setImage(e.target.value)} value={image} className="input input-bordered input-accent w-full max-w-xs" type="text" placeholder="/images/1.jpg" defaultValue="/images/1.jpg" /> <input onChange={(e) => setPrice(e.target.value)} value={price} className="input input-bordered input-accent w-full max-w-xs" type="number" placeholder="1" defaultValue="1" /> <input onChange={(e) => setCategory(e.target.value)} value={category} className="input input-bordered input-accent w-full max-w-xs" type="text" placeholder="Product Category" /> <button type="submit" className="btn btn-primary w-full max-w-xs" > Add Product </button> </form> </> ); }app\editProduct\[id]\page.js
//app\editProduct\[id]\page.js import EditProductForm from "@/components/EditProductForm"; const getProductById = async (id) => { try { const res = await fetch(`http://localhost:3000/api/products/${id}`, { cache: "no-store", }); if (!res.ok) { throw new Error("Failed to fetch product"); } return res.json(); } catch (error) { console.log(error); } }; export default async function EditProduct({ params }) { const { id } = params; const { product } = await getProductById(id); const { name, image, price,category } = product; return <EditProductForm id={id} name={name} image={image} price={price} category={category} />; }app\api\products\route.js
//app\api\products\route.js import connectMongoDB from "@/libs/mongodb"; import Product from "@/models/ProductModel"; import { NextResponse } from "next/server"; export async function GET() { await connectMongoDB(); const products = await Product.find(); return NextResponse.json({ products }); } export async function POST(request) { const { name, image,price,category } = await request.json(); await connectMongoDB(); await Product.create({ name, image, price, category }); return NextResponse.json({ message: "Product Created" }, { status: 201 }); } export async function DELETE(request) { const id = request.nextUrl.searchParams.get("id"); await connectMongoDB(); await Product.findByIdAndDelete(id); return NextResponse.json({ message: "Product deleted" }, { status: 200 }); }app\api\products\[id]\route.js
//app\api\products\[id]\route.js import connectMongoDB from "@/libs/mongodb"; import Product from "@/models/ProductModel"; import { NextResponse } from "next/server"; export async function PUT(request, { params }) { const { id } = params; const { newName: name, newImage: image, newPrice: price, newCategory: category } = await request.json(); await connectMongoDB(); await Product.findByIdAndUpdate(id, { name, image, price, category}); return NextResponse.json({ message: "Product updated" }, { status: 200 }); } export async function GET(request, { params }) { const { id } = params; await connectMongoDB(); const product = await Product.findOne({ _id: id }); return NextResponse.json({ product }, { status: 200 }); }run C:\nextjs>npm run dev