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
NextAuth.js
npm install next-auth
https://www.npmjs.com/package/next-auth/v/4.23.1
NextAuth.js is a complete open source authentication solution for Next.js applications.
bcrypt.js
npm install bcryptjs
https://www.npmjs.com/package/bcryptjs
edit tailwind.config.js 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/nextjs14 NEXTAUTH_SECRET=cairocodersapp\layout.tsx
//app\layout.js import Navbar from "@/components/Navbar"; import "./globals.css"; import type { Metadata } from "next"; import { Inter } from "next/font/google"; import { getServerSession } from "next-auth"; import SessionProvider from "@/utils/SessionProvider"; const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { title: "Next.js 14 Login Register MongoDB Next-Auth Login Authentication Daisyui Tailwind", description: "Generated by create next app", }; export default async function RootLayout({ children, }: { children: React.ReactNode; }) { const session = await getServerSession(); return ( <html lang="en"> <body className={inter.className}> <SessionProvider session={session}> <div className="container mx-auto px-4"> <Navbar /> {children} </div> </SessionProvider> </body> </html> ); }components\Navbar.tsx
//components\Navbar.jsx "use client"; import React from "react"; import Link from "next/link"; import { signOut, useSession } from "next-auth/react"; const Navbar = () => { const { data: session }: any = useSession(); return ( <div className="navbar bg-base-100"> <div className="flex-1"> <a className="btn btn-ghost text-xl">Cairocoders</a> <Link href="/"> Home </Link> </div> <div className="flex-none"> {!session ? ( <> <Link href="/login" className="btn btn-primary"> Login </Link> <Link href="/register" className="btn btn-secondary ml-2"> Register </Link> </> ) : ( <> {session.user?.email} <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> <button onClick={() => { signOut(); }} > Logout </button> </li> </ul> </div> </> )} </div> </div> ); }; export default Navbar;models\User.js
//models\User.js import mongoose from "mongoose"; const { Schema } = mongoose; const userSchema = new Schema( { name: { type: String, required: true, }, email: { type: String, unique: true, required: true, }, password: { type: String, required: false, }, }, { timestamps: true } ); export default mongoose.models.User || mongoose.model("User", userSchema);utils\db.js
//utils\db.js import mongoose from "mongoose"; const connect = async () => { if (mongoose.connections[0].readyState) return; try { await mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true, }); console.log("Mongo Connection successfully established."); } catch (error) { throw new Error("Error connecting to Mongoose"); } }; export default connect;utils\SessionProvider.tsx
//utils\SessionProvider.tsx "use client"; import React from "react"; import { SessionProvider } from "next-auth/react"; //https://www.npmjs.com/package/next-auth/v/4.23.1 const AuthProvider = ({ children }: any) => { returnapp\pag.tsx{children} ; }; export default AuthProvider;
//app\pag.tsx export default function Home() { return ( <main> <div className="carousel w-full"> <div id="item1" className="carousel-item w-full"> <img src="https://daisyui.com/images/stock/photo-1625726411847-8cbb60cc71e6.jpg" className="w-full" /> </div> <div id="item2" className="carousel-item w-full"> <img src="https://daisyui.com/images/stock/photo-1609621838510-5ad474b7d25d.jpg" className="w-full" /> </div> <div id="item3" className="carousel-item w-full"> <img src="https://daisyui.com/images/stock/photo-1414694762283-acccc27bca85.jpg" className="w-full" /> </div> <div id="item4" className="carousel-item w-full"> <img src="https://daisyui.com/images/stock/photo-1665553365602-b2fb8e5d1707.jpg" className="w-full" /> </div> </div> <div className="flex justify-center w-full py-2 gap-2"> <a href="#item1" className="btn btn-xs">1</a> <a href="#item2" className="btn btn-xs">2</a> <a href="#item3" className="btn btn-xs">3</a> <a href="#item4" className="btn btn-xs">4</a> </div> </main> ); }app\login\page.tsx
//app\login\page.tsx "use client"; import React, { useEffect, useState } from "react"; import Link from "next/link"; import { signIn, useSession } from "next-auth/react"; import { useRouter } from "next/navigation"; const Login = () => { const router = useRouter(); const [error, setError] = useState(""); const { data: session, status: sessionStatus } = useSession(); useEffect(() => { if (sessionStatus === "authenticated") { router.replace("/dashboard"); } }, [sessionStatus, router]); const isValidEmail = (email: string) => { const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i; return emailRegex.test(email); }; const handleSubmit = async (e: any) => { e.preventDefault(); const email = e.target[0].value; const password = e.target[1].value; if (!isValidEmail(email)) { setError("Email is invalid"); return; } if (!password || password.length < 8) { setError("Password is invalid"); return; } const res = await signIn("credentials", { redirect: false, email, password, }); if (res?.error) { setError("Invalid email or password"); if (res?.url) router.replace("/dashboard"); } else { setError(""); } }; if (sessionStatus === "loading") { return <h1>Loading...</h1>; } return ( sessionStatus !== "authenticated" && ( <div className="justify-center mt-16"> <div className="w-full p-6 m-auto bg-white rounded-md shadow-md lg:max-w-lg"> <h1 className="text-3xl font-semibold text-center text-purple-700">Login</h1> <form onSubmit={handleSubmit} className="space-y-4"> <div> <label className="label"> <span className="text-base label-text">Email</span> </label> <input type="text" placeholder="Email Address" required className="w-full input input-bordered input-primary" /> </div> <div> <label className="label"> <span className="text-base label-text">Password</span> </label> <input type="password" placeholder="Enter Password" required className="w-full input input-bordered input-primary" /> </div> <a href="#" className="text-xs text-gray-600 hover:underline hover:text-blue-600">Forget Password?</a> <div> <button type="submit" className="btn btn-primary" > {" "} Sign In </button> <p className="text-red-600 text-[16px] mb-4">{error && error}</p> </div> </form> <Link className="block text-center text-blue-500 hover:underline mt-2" href="/register" > Register Here </Link> </div> </div> ) ); }; export default Login;app\register\page.tsx
//app\register\page.tsx "use client"; import React, { useEffect, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useSession } from "next-auth/react"; const Register = () => { const [error, setError] = useState(""); const router = useRouter(); const { data: session, status: sessionStatus } = useSession(); useEffect(() => { if (sessionStatus === "authenticated") { router.replace("/dashboard"); } }, [sessionStatus, router]); const isValidEmail = (email: string) => { const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i; return emailRegex.test(email); }; const handleSubmit = async (e: any) => { e.preventDefault(); const name = e.target[0].value; const email = e.target[1].value; const password = e.target[2].value; if (!isValidEmail(email)) { setError("Email is invalid"); return; } if (!password || password.length < 8) { setError("Password is invalid"); return; } try { const res = await fetch("/api/register", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ name, email, password, }), }); if (res.status === 400) { setError("This email is already registered"); } if (res.status === 200) { setError(""); router.push("/login"); } } catch (error) { setError("Error, try again"); console.log(error); } }; if (sessionStatus === "loading") { return <h1>Loading...</h1>; } return ( sessionStatus !== "authenticated" && ( <div className="justify-center mt-16"> <div className="w-full p-6 m-auto bg-white rounded-md shadow-md lg:max-w-lg"> <h1 className="text-3xl font-semibold text-center text-purple-700">Register</h1> <form onSubmit={handleSubmit} className="space-y-4"> <div> <label className="label"> <span className="text-base label-text">Name</span> </label> <input type="text" placeholder="Name" required className="w-full input input-bordered input-primary" /> </div> <div> <label className="label"> <span className="text-base label-text">Email</span> </label> <input type="text" placeholder="Email Address" required className="w-full input input-bordered input-primary" /> </div> <div> <label className="label"> <span className="text-base label-text">Password</span> </label> <input type="password" placeholder="Enter Password" required className="w-full input input-bordered input-primary" /> </div> <button type="submit" className="btn btn-primary" > {" "} Register </button> <p className="text-red-600 text-[16px] mb-4">{error && error}</p> </form> <div className="text-center text-gray-500 mt-4">- OR -</div> <Link className="block text-center text-blue-500 hover:underline mt-2" href="/login" > Login with an existing account </Link> </div> </div> ) ); }; export default Register;app\api\register\route.ts
//app\api\register\route.ts import User from "@/models/User"; import connect from "@/utils/db"; import bcrypt from "bcryptjs"; //https://www.npmjs.com/package/bcryptjs npm install bcryptjs import { NextResponse } from "next/server"; export const POST = async (request: any) => { const { name, email, password } = await request.json(); await connect(); const existingUser = await User.findOne({ email }); if (existingUser) { return new NextResponse("Email is already in use", { status: 400 }); } const hashedPassword = await bcrypt.hash(password, 5); const newUser = new User({ name, email, password: hashedPassword, }); try { await newUser.save(); return new NextResponse("user is registered", { status: 200 }); } catch (err: any) { return new NextResponse(err, { status: 500, }); } };app\api\auth\[...nextauth]\route.ts
//app\api\auth\[...nextauth]\route.ts import NextAuth from "next-auth"; import { Account, User as AuthUser } from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; import bcrypt from "bcryptjs"; import User from "@/models/User"; import connect from "@/utils/db"; export const authOptions: any = { // Configure one or more authentication providers secret: process.env.NEXTAUTH_SECRET, providers: [ CredentialsProvider({ id: "credentials", name: "Credentials", credentials: { email: { label: "Email", type: "text" }, password: { label: "Password", type: "password" }, }, async authorize(credentials: any) { await connect(); try { const user = await User.findOne({ email: credentials.email }); if (user) { const isPasswordCorrect = await bcrypt.compare( credentials.password, user.password ); if (isPasswordCorrect) { return user; } } } catch (err: any) { throw new Error(err); } }, }), ], callbacks: { async signIn({ user, account }: { user: AuthUser; account: Account }) { if (account?.provider == "credentials") { return true; } }, }, }; export const handler = NextAuth(authOptions); export { handler as GET, handler as POST };run C:\nextjs>npm run dev