First open up VS code and run this command to create a new nextjs 15 projet.
we'll use pnpm
to avoid writing --legacy-peer-deps
for all installations.
pnpx create-next-app@latest
The settings will be like that;
√ Would you like to use TypeScript? ... No / Yes
√ Would you like to use ESLint? ... No / Yes
√ Would you like to use Tailwind CSS? ... No / Yes
√ Would you like your code inside a `src/` directory? ... No / Yes
√ Would you like to use App Router? (recommended) ... No / Yes
√ Would you like to use Turbopack for next dev? ... No / Yes
? Would you like to customize the import alias (@/* by default)? » No / Yes
Our package.json file will looks like;
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"next": "15.0.4"
},
Adding Poppins Font
Now create a fonts.tsx file inside app/fonts/fonts.tsx we'll add Poppins font from Google
// app/fonts/fonts.tsx
import { Poppins } from "next/font/google"
const poppins = Poppins({
subsets: ["latin-ext"],
weight: ["400", "500", "600"],
})
export { poppins }
Now import the poppins fort to the Root Layout file app/layout.tsx.
// app/layout.tsx
import type { Metadata } from "next"
import { poppins } from "@/app/fonts/fonts"
export const metadata: Metadata = {
title: "Next Auth V5 With Magic Link Resend Api",
description:
"We'll implement auth js beta wit resend provider and magic link in this whole tutorial. Step by step this guide will help you to integrate auth js with Nextjs 15.",
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en">
<body className={`${poppins.className} antialiased`}>{children}</body>
</html>
)
}
Now we'll imlement navbar component create a comppnents folder in root directory and create frontend-ui/header foldres in it.
/components/frontend-ui/header/navbar.tsx
import Link from "next/link"
import { AuthLinks } from "./auth-links"
import { NavbarRoutes } from "./navbar-routes"
export default function Navbar() {
return (
<div className="h-16 relative">
<div className="h-16 border-b fixed top-0 lef-0 w-full flex items-center">
<div className="max-w-[1400px] w-full mx-auto px-4 flex items-center gap-x-4">
<Link href="/" className="text-3xl font-semibold tracking-tighter">
AppLogo
</Link>
{/* Navbar Routes */}
<NavbarRoutes />
{/*auth links */}
<AuthLinks />
</div>
</div>
</div>
)
}
Now create <NavbarRoutes /> and <AuthLinks /> component inside of header folder
create <NavbarRoutes /> -→ /components/frontend-ui/header/navbar-routes.tsx
// /components/frontend-ui/header/navbar-routes.tsx
"use client"
import clsx from "clsx"
import Link from "next/link"
import * as css from "@/app/styles/css"
import { usePathname } from "next/navigation"
interface MenuTypes {
id: string
value: string
url: string
}
export const NavbarRoutes = () => {
const pathname = usePathname()
// to avoid hydration error when pathname changed.
useEffect(() => {}, [pathname])
const routes: MenuTypes[] = [
{
id: "1",
value: "HOME",
url: "/",
},
{
id: "2",
value: "POSTS",
url: "/blog",
},
]
return (
<nav className="ml-auto md:px-6">
<ul className="flex items-center font-semibold text-sm gap-x-4">
{routes.map((route) => (
<li key={route.id}>
<Link
className={clsx(
pathname === route.url ? "opacity-100" : "opacity-60",
css.navLink
)}
href={route.url}
>
{route.value}
</Link>
</li>
))}
</ul>
</nav>
)
}
create <AuthLinks /> -→ /components/frontend-ui/header/auth-links.tsx
// /components/frontend-ui/header/auth-links.tsx
import Link from "next/link"
import * as css from "@/app/styles/css"
export const AuthLinks = () => {
return (
<nav className="flex items-center gap-x-2">
<Link className={css.outlineButton} href="/auth/login">
LOGIN
</Link>
<Link className={css.darkButton} href="/auth/login">
SIGN UP
</Link>
</nav>
)
}
Create a styles folder inside of your app directory for css styles
// /app/styles/css.ts
export const navLink =
"font-semibold px-2 py-4 transition-all hover:opacity-100"
export const outlineButton =
"border font-medium tracking-tight px-4 py-2 text-xs border-slate-300 h-10 flex items-center"
export const darkButton =
"border border-transparent font-medium tracking-tight px-4 py-2 text-xs bg-black/90 text-white h-10 flex items-center"
Add Navbar componet to the RootLayout -→ /app/layout.tsx
// /app/layout.tsx
import type { Metadata } from "next"
import { poppins } from "@/app/fonts/fonts"
import Navbar from "@/components/frontend-ui/header/navbar"
import "@/app/styles/globals.css"
export const metadata: Metadata = {
title: "Next Auth V5 With Magic Link Resend Api",
description:
"We'll implement auth js beta wit resend provider and magic link in this whole tutorial. Step by step this guide will help you to integrate auth js with Nextjs 15.",
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en">
<body className={`${poppins.className} antialiased`}>
<Navbar />
{children}
</body>
</html>
)
}
Create Blog Page
Now create blog directory in your app folder and create page.tsx inside of it
// app/blog/page.tsx
export default function PostsPage() {
return <div>Posts Page</div>
}
Start Your Project
pnpm run dev
Your projects looks like that
Admin Layout created -→ now stop your application from the terminal and organize your folders like that. I will create it later -→
Postgres and Neon Serverless Integration
pnpm add pg
pnpm add -D @types/pg
with this commands we install pg and pg types for typescript.
notice that: your packagejson file will be like that;
"dependencies": {
...others
"pg": "^8.13.1",
},
"devDependencies": {
...others
"@types/pg": "^8.11.10",
}
Şimdi Next Auth V5 kurulumu yapalım
pnpm i next-auth@beta
You need a AUTH_SECRET key in your .env file
to get an AUTH_SECRET run this command
pnpx auth secret
This command will create .env.local file and will put a AUTH_SCRET key variable inside of it. copy and paste it into your .env file Like that AUTH_SECRET=<YOUR_AUTH_SECRET_KEY>
Neon Postgresql integration
Go to neon.tech website and create an account and create a db. Choose the connection string for Next.js. Neon console will give you code like that;
DATABASE_URL="postgresql://<user>:<password>@<endpoint_hostname>.neon.tech:<port>/<dbname>?sslmode=require"
Copy paste this code to your .env file. Do not forget .gitignore if .nv not added -→
# env files (can opt-in for committing if needed)
.env*
Now install @neondatabase/serverless
pnpm install @neondatabase/serverless
Create db.ts
Create a new folder lib and inside lib folder create db.ts file.
// lib/db.ts
import { Pool } from "@neondatabase/serverless"
import type { Pool as PgPool } from "pg"
export const pool = new Pool({
connectionString: process.env.DATABASE_URL!,
ssl: true,
connectionTimeoutMillis: 5000,
idleTimeoutMillis: 5000,
max: 20,
}) as unknown as PgPool
Creating Tables
Now Create Tables on Neon Console with sql queries. Create a file schema.sql inside root directory of your project. This will allow you to see all schemas whenever you need it.
schema.sql file
run this sql queries inside of neon console. Click the SQL Editor link on the left hand of the screen at the left side bar. and copy past all of these sql codes to create your tables
After it finished successfully you will have 4 tables
- accounst
- sessions
- users
- verification_token
note: Now we no longer use accounts table through this tutorial. accounts table is needed when you want to create oAuth logins like google and github in your project. All right!
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
"emailVerified" TIMESTAMPTZ,
verified BOOLEAN DEFAULT FALSE NOT NULL,
name VARCHAR(255),
image TEXT,
role VARCHAR(10) CHECK (role IN ('user', 'admin')) DEFAULT 'user' NOT NULL,
password TEXT NOT NULL,
last_login_ip VARCHAR(45),
last_user_agent TEXT,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL
);
CREATE INDEX users_email_idx ON users(email);
CREATE TABLE accounts (
id SERIAL,
"userId" INTEGER NOT NULL,
type VARCHAR(255) NOT NULL,
provider VARCHAR(255) NOT NULL,
"providerAccountId" VARCHAR(255) NOT NULL,
refresh_token TEXT,
access_token TEXT,
expires_at BIGINT,
id_token TEXT,
scope TEXT,
session_state TEXT,
token_type TEXT,
PRIMARY KEY (id)
);
CREATE TABLE sessions (
id SERIAL,
"userId" INTEGER NOT NULL,
expires TIMESTAMPTZ NOT NULL,
"sessionToken" VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE verification_token (
identifier TEXT NOT NULL,
expires TIMESTAMPTZ NOT NULL,
token TEXT NOT NULL,
PRIMARY KEY (identifier, token)
);