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) => {
return {children} ;
};
export default AuthProvider;
app\pag.tsx
//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
