├── authentication-demo
├── .eslintrc.json
├── .gitignore
├── README.md
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
│ ├── file.svg
│ ├── globe.svg
│ ├── next.svg
│ ├── vercel.svg
│ └── window.svg
├── src
│ ├── app
│ │ ├── admin
│ │ │ ├── actions.ts
│ │ │ └── page.tsx
│ │ ├── dashboard
│ │ │ └── page.tsx
│ │ ├── favicon.ico
│ │ ├── fonts
│ │ │ ├── GeistMonoVF.woff
│ │ │ └── GeistVF.woff
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ ├── sign-in
│ │ │ └── [[...sign-in]]
│ │ │ │ └── page.tsx
│ │ ├── sign-up
│ │ │ └── [[...sign-up]]
│ │ │ │ └── page.tsx
│ │ └── user-profile
│ │ │ └── [[...user-profile]]
│ │ │ └── page.tsx
│ ├── components
│ │ ├── counter.tsx
│ │ └── navigation.tsx
│ └── middleware.ts
├── tailwind.config.ts
├── tsconfig.json
└── types
│ └── globals.d.ts
├── caching-revalidation-demo
├── .eslintrc.json
├── .gitignore
├── README.md
├── db.json
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── prisma
│ ├── app.db
│ ├── migrations
│ │ ├── 20241125081652_init
│ │ │ └── migration.sql
│ │ └── migration_lock.toml
│ └── schema.prisma
├── public
│ ├── file.svg
│ ├── globe.svg
│ ├── next.svg
│ ├── vercel.svg
│ └── window.svg
├── src
│ ├── app
│ │ ├── favicon.ico
│ │ ├── fonts
│ │ │ ├── GeistMonoVF.woff
│ │ │ └── GeistVF.woff
│ │ ├── globals.css
│ │ ├── json-server-products
│ │ │ └── page.tsx
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ └── prisma-products
│ │ │ └── page.tsx
│ └── prisma-db.ts
├── tailwind.config.ts
└── tsconfig.json
├── data-fetching-demo
├── .eslintrc.json
├── .gitignore
├── README.md
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── prisma
│ ├── migrations
│ │ ├── 20241119043535_init
│ │ │ └── migration.sql
│ │ └── migration_lock.toml
│ └── schema.prisma
├── public
│ ├── file.svg
│ ├── globe.svg
│ ├── next.svg
│ ├── vercel.svg
│ └── window.svg
├── src
│ ├── actions
│ │ └── products.ts
│ ├── app
│ │ ├── favicon.ico
│ │ ├── fonts
│ │ │ ├── GeistMonoVF.woff
│ │ │ └── GeistVF.woff
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ ├── posts-sequential
│ │ │ ├── author.tsx
│ │ │ └── page.tsx
│ │ ├── products-db-create
│ │ │ └── page.tsx
│ │ ├── products-db
│ │ │ ├── [id]
│ │ │ │ ├── page.tsx
│ │ │ │ └── product-edit-form.tsx
│ │ │ ├── page.tsx
│ │ │ └── product-detail.tsx
│ │ ├── react-form
│ │ │ ├── api
│ │ │ │ └── route.ts
│ │ │ └── page.tsx
│ │ ├── user-parallel
│ │ │ └── [userId]
│ │ │ │ ├── loading.tsx
│ │ │ │ └── page.tsx
│ │ ├── users-client
│ │ │ └── page.tsx
│ │ └── users-server
│ │ │ ├── error.tsx
│ │ │ ├── loading.tsx
│ │ │ └── page.tsx
│ ├── components
│ │ ├── search.tsx
│ │ └── submit.tsx
│ └── prisma-db.ts
├── tailwind.config.ts
└── tsconfig.json
├── hello-world
├── .eslintrc.json
├── .gitignore
├── README.md
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
│ ├── file.svg
│ ├── globe.svg
│ ├── next.svg
│ ├── vercel.svg
│ └── window.svg
├── src
│ └── app
│ │ ├── favicon.ico
│ │ ├── fonts
│ │ ├── GeistMonoVF.woff
│ │ └── GeistVF.woff
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ └── page.tsx
├── tailwind.config.ts
└── tsconfig.json
├── misc-demo
├── .eslintrc.json
├── .gitignore
├── README.md
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
│ ├── file.svg
│ ├── globe.svg
│ ├── next.svg
│ ├── vercel.svg
│ └── window.svg
├── src
│ └── app
│ │ ├── about
│ │ ├── about.module.css
│ │ ├── about.module.scss
│ │ └── page.tsx
│ │ ├── colors.scss
│ │ ├── contact
│ │ ├── contact.module.css
│ │ ├── contact.module.scss
│ │ └── page.tsx
│ │ ├── favicon.ico
│ │ ├── fonts
│ │ ├── GeistMonoVF.woff
│ │ └── GeistVF.woff
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ └── page.tsx
├── tailwind.config.ts
└── tsconfig.json
├── rendering-demo
├── .eslintrc.json
├── .gitignore
├── README.md
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
│ ├── file.svg
│ ├── globe.svg
│ ├── next.svg
│ ├── vercel.svg
│ └── window.svg
├── src
│ ├── app
│ │ ├── about
│ │ │ └── page.tsx
│ │ ├── client-route
│ │ │ └── page.tsx
│ │ ├── dashboard
│ │ │ └── page.tsx
│ │ ├── favicon.ico
│ │ ├── fonts
│ │ │ ├── GeistMonoVF.woff
│ │ │ └── GeistVF.woff
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ ├── product-reviews
│ │ │ └── page.tsx
│ │ ├── products
│ │ │ ├── [id]
│ │ │ │ └── page.tsx
│ │ │ └── page.tsx
│ │ └── server-route
│ │ │ └── page.tsx
│ ├── components
│ │ ├── ImageSlider.tsx
│ │ ├── product.tsx
│ │ ├── reviews.tsx
│ │ └── theme-provider.tsx
│ └── utils
│ │ └── server-utils.ts
├── tailwind.config.ts
└── tsconfig.json
├── route-handlers-demo
├── .eslintrc.json
├── .gitignore
├── README.md
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
│ ├── file.svg
│ ├── globe.svg
│ ├── next.svg
│ ├── vercel.svg
│ └── window.svg
├── src
│ └── app
│ │ ├── api
│ │ ├── v1
│ │ │ └── users
│ │ │ │ └── route.ts
│ │ └── v2
│ │ │ └── users
│ │ │ └── route.ts
│ │ ├── categories
│ │ └── route.ts
│ │ ├── comments
│ │ ├── [id]
│ │ │ └── route.ts
│ │ ├── data.ts
│ │ └── route.ts
│ │ ├── dashboard
│ │ ├── route.ts
│ │ └── users
│ │ │ └── route.ts
│ │ ├── favicon.ico
│ │ ├── fonts
│ │ ├── GeistMonoVF.woff
│ │ └── GeistVF.woff
│ │ ├── globals.css
│ │ ├── hello
│ │ └── route.ts
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ ├── profile
│ │ ├── api
│ │ │ └── route.ts
│ │ └── page.tsx
│ │ └── time
│ │ └── route.ts
├── tailwind.config.ts
└── tsconfig.json
└── routing-demo
├── .eslintrc.json
├── .gitignore
├── README.md
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
├── file.svg
├── globe.svg
├── next.svg
├── vercel.svg
└── window.svg
├── src
├── app
│ ├── (auth)
│ │ ├── forgot-password
│ │ │ └── page.tsx
│ │ ├── login
│ │ │ └── page.tsx
│ │ ├── register
│ │ │ └── page.tsx
│ │ ├── styles.css
│ │ └── template.tsx
│ ├── _lib
│ │ ├── format-date.js
│ │ └── page.tsx
│ ├── about
│ │ └── page.tsx
│ ├── articles
│ │ └── [articleId]
│ │ │ └── page.tsx
│ ├── blog
│ │ ├── first
│ │ │ └── page.tsx
│ │ ├── page.tsx
│ │ └── second
│ │ │ └── page.tsx
│ ├── complex-dashboard
│ │ ├── @login
│ │ │ └── page.tsx
│ │ ├── @notifications
│ │ │ ├── archived
│ │ │ │ └── page.tsx
│ │ │ └── page.tsx
│ │ ├── @revenue
│ │ │ ├── default.tsx
│ │ │ └── page.tsx
│ │ ├── @users
│ │ │ ├── default.tsx
│ │ │ └── page.tsx
│ │ ├── default.tsx
│ │ ├── layout.tsx
│ │ └── page.tsx
│ ├── counter
│ │ ├── counter.tsx
│ │ └── page.tsx
│ ├── dashboard
│ │ ├── line-chart.tsx
│ │ └── page.tsx
│ ├── docs
│ │ └── [[...slug]]
│ │ │ └── page.tsx
│ ├── error-wrapper.tsx
│ ├── f1
│ │ ├── (.)f2
│ │ │ └── page.tsx
│ │ ├── (..)f3
│ │ │ └── page.tsx
│ │ ├── f2
│ │ │ ├── (..)(..)f4
│ │ │ │ └── page.tsx
│ │ │ ├── inner-f2
│ │ │ │ ├── (...)f5
│ │ │ │ │ └── page.tsx
│ │ │ │ └── page.tsx
│ │ │ └── page.tsx
│ │ └── page.tsx
│ ├── f3
│ │ └── page.tsx
│ ├── f4
│ │ └── page.tsx
│ ├── f5
│ │ └── page.tsx
│ ├── global-error.tsx
│ ├── globals.css
│ ├── layout.tsx
│ ├── not-found.tsx
│ ├── page.tsx
│ ├── photo-feed
│ │ ├── @modal
│ │ │ ├── (.)[id]
│ │ │ │ └── page.tsx
│ │ │ └── default.tsx
│ │ ├── [id]
│ │ │ └── page.tsx
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ ├── photos
│ │ │ ├── 1.jpg
│ │ │ ├── 2.jpg
│ │ │ ├── 3.jpg
│ │ │ ├── 4.jpg
│ │ │ ├── 5.jpg
│ │ │ ├── 6.jpg
│ │ │ └── 7.jpg
│ │ ├── styles.css
│ │ └── wonders.ts
│ ├── products
│ │ ├── [productId]
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx
│ │ │ └── reviews
│ │ │ │ └── [reviewId]
│ │ │ │ ├── error.tsx
│ │ │ │ ├── not-found.tsx
│ │ │ │ └── page.tsx
│ │ ├── error.tsx
│ │ └── page.tsx
│ └── profile
│ │ └── page.tsx
└── components
│ ├── card.tsx
│ └── modal.tsx
├── tailwind.config.ts
└── tsconfig.json
/authentication-demo/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/core-web-vitals", "next/typescript"]
3 | }
4 |
--------------------------------------------------------------------------------
/authentication-demo/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for committing if needed)
33 | .env*
34 |
35 | # vercel
36 | .vercel
37 |
38 | # typescript
39 | *.tsbuildinfo
40 | next-env.d.ts
41 |
--------------------------------------------------------------------------------
/authentication-demo/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
37 |
--------------------------------------------------------------------------------
/authentication-demo/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | };
6 |
7 | export default nextConfig;
8 |
--------------------------------------------------------------------------------
/authentication-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "authentication-demo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@clerk/nextjs": "^6.5.1",
13 | "next": "15.0.3",
14 | "react": "19.0.0-rc-66855b96-20241106",
15 | "react-dom": "19.0.0-rc-66855b96-20241106"
16 | },
17 | "devDependencies": {
18 | "@types/node": "^20",
19 | "@types/react": "^18",
20 | "@types/react-dom": "^18",
21 | "eslint": "^8",
22 | "eslint-config-next": "15.0.3",
23 | "postcss": "^8",
24 | "tailwindcss": "^3.4.1",
25 | "typescript": "^5"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/authentication-demo/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/authentication-demo/public/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/authentication-demo/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/authentication-demo/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/authentication-demo/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/authentication-demo/public/window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/authentication-demo/src/app/admin/actions.ts:
--------------------------------------------------------------------------------
1 | "use server";
2 | import { auth } from "@clerk/nextjs/server";
3 | import { clerkClient } from "@clerk/nextjs/server";
4 | import { Roles } from "../../../types/globals";
5 | import { revalidatePath } from "next/cache";
6 |
7 | export async function setRole(formData: FormData) {
8 | const { sessionClaims } = await auth();
9 |
10 | // Check that the user trying to set the role is an admin
11 | if (sessionClaims?.metadata?.role !== "admin") {
12 | throw new Error("Not Authorized");
13 | }
14 |
15 | const client = await clerkClient();
16 | const id = formData.get("id") as string;
17 | const role = formData.get("role") as Roles;
18 |
19 | try {
20 | await client.users.updateUser(id, {
21 | publicMetadata: { role },
22 | });
23 | revalidatePath("/admin");
24 | } catch {
25 | throw new Error("Failed to set role");
26 | }
27 | }
28 |
29 | export async function removeRole(formData: FormData) {
30 | const { sessionClaims } = await auth();
31 |
32 | if (sessionClaims?.metadata?.role !== "admin") {
33 | throw new Error("Not Authorized");
34 | }
35 |
36 | const client = await clerkClient();
37 | const id = formData.get("id") as string;
38 |
39 | try {
40 | await client.users.updateUser(id, {
41 | publicMetadata: { role: null },
42 | });
43 | revalidatePath("/admin");
44 | } catch {
45 | throw new Error("Failed to remove role");
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/authentication-demo/src/app/admin/page.tsx:
--------------------------------------------------------------------------------
1 | import { clerkClient } from "@clerk/nextjs/server";
2 | import { removeRole, setRole } from "./actions";
3 |
4 | export default async function Admin() {
5 | const client = await clerkClient();
6 |
7 | const users = (await client.users.getUserList()).data;
8 |
9 | return (
10 | <>
11 | {users.map((user) => {
12 | return (
13 |
21 |
22 |
23 | {user.firstName} {user.lastName}
24 |
25 |
26 |
27 | {
28 | user.emailAddresses.find(
29 | (email) => email.id === user.primaryEmailAddressId
30 | )?.emailAddress
31 | }
32 |
33 |
34 |
35 | {user.publicMetadata.role as string}
36 |
37 |
38 |
39 |
40 |
50 |
51 |
61 |
62 |
71 |
72 |
73 | );
74 | })}
75 | >
76 | );
77 | }
78 |
--------------------------------------------------------------------------------
/authentication-demo/src/app/dashboard/page.tsx:
--------------------------------------------------------------------------------
1 | import { auth, currentUser } from "@clerk/nextjs/server";
2 |
3 | export default async function DashboardPage() {
4 | const authObj = await auth();
5 | const userObj = await currentUser();
6 |
7 | console.log({ authObj });
8 | console.log({ userObj });
9 |
10 | return Dashboard
;
11 | }
12 |
--------------------------------------------------------------------------------
/authentication-demo/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/authentication-demo/src/app/favicon.ico
--------------------------------------------------------------------------------
/authentication-demo/src/app/fonts/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/authentication-demo/src/app/fonts/GeistMonoVF.woff
--------------------------------------------------------------------------------
/authentication-demo/src/app/fonts/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/authentication-demo/src/app/fonts/GeistVF.woff
--------------------------------------------------------------------------------
/authentication-demo/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --background: #ffffff;
7 | --foreground: #171717;
8 | }
9 |
10 | @media (prefers-color-scheme: dark) {
11 | :root {
12 | --background: #0a0a0a;
13 | --foreground: #ededed;
14 | }
15 | }
16 |
17 | body {
18 | color: var(--foreground);
19 | background: var(--background);
20 | font-family: Arial, Helvetica, sans-serif;
21 | }
22 |
--------------------------------------------------------------------------------
/authentication-demo/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import { ClerkProvider } from "@clerk/nextjs";
2 | import type { Metadata } from "next";
3 | import localFont from "next/font/local";
4 | import "./globals.css";
5 | import { Navigation } from "@/components/navigation";
6 |
7 | const geistSans = localFont({
8 | src: "./fonts/GeistVF.woff",
9 | variable: "--font-geist-sans",
10 | weight: "100 900",
11 | });
12 | const geistMono = localFont({
13 | src: "./fonts/GeistMonoVF.woff",
14 | variable: "--font-geist-mono",
15 | weight: "100 900",
16 | });
17 |
18 | export const metadata: Metadata = {
19 | title: "Create Next App",
20 | description: "Generated by create next app",
21 | };
22 |
23 | export default function RootLayout({
24 | children,
25 | }: Readonly<{
26 | children: React.ReactNode;
27 | }>) {
28 | return (
29 |
30 |
31 |
34 |
35 | {children}
36 |
37 |
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/authentication-demo/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import { Counter } from "@/components/counter";
3 | export default function Home() {
4 | return (
5 |
6 |
7 |
15 |
16 |
17 | -
18 | Get started by editing{" "}
19 |
20 | src/app/page.tsx
21 |
22 | .
23 |
24 | - Save and see your changes instantly.
25 |
26 |
27 |
52 |
53 |
100 |
101 | );
102 | }
103 |
--------------------------------------------------------------------------------
/authentication-demo/src/app/sign-in/[[...sign-in]]/page.tsx:
--------------------------------------------------------------------------------
1 | import { SignIn } from "@clerk/nextjs";
2 |
3 | export default function SignInPage() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/authentication-demo/src/app/sign-up/[[...sign-up]]/page.tsx:
--------------------------------------------------------------------------------
1 | import { SignUp } from "@clerk/nextjs";
2 |
3 | export default function SignUpPage() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/authentication-demo/src/app/user-profile/[[...user-profile]]/page.tsx:
--------------------------------------------------------------------------------
1 | import { UserProfile } from "@clerk/nextjs";
2 |
3 | export default function UserProfilePage() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/authentication-demo/src/components/counter.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState } from "react";
4 | import {
5 | useAuth,
6 | // useUser
7 | } from "@clerk/nextjs";
8 |
9 | export const Counter = () => {
10 | const [count, setCount] = useState(0);
11 | const {
12 | isLoaded,
13 | userId,
14 | // sessionId, getToken
15 | } = useAuth();
16 | // const { isLoaded, isSignedIn, user } = useUser();
17 |
18 | if (!isLoaded || !userId) {
19 | return null;
20 | }
21 |
22 | // if (!isLoaded || !isSignedIn) {
23 | // return null;
24 | // }
25 | return (
26 | <>
27 | Count: {count}
28 |
29 | >
30 | );
31 | };
32 |
--------------------------------------------------------------------------------
/authentication-demo/src/components/navigation.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | SignInButton,
3 | SignOutButton,
4 | SignUpButton,
5 | // UserButton,
6 | SignedIn,
7 | SignedOut,
8 | } from "@clerk/nextjs";
9 | import Link from "next/link";
10 | export const Navigation = () => {
11 | return (
12 |
42 | );
43 | };
44 |
--------------------------------------------------------------------------------
/authentication-demo/src/middleware.ts:
--------------------------------------------------------------------------------
1 | import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
2 | import { NextResponse } from "next/server";
3 |
4 | // const isProtectedRoute = createRouteMatcher(["/user-profile"]);
5 | const isPublicRoute = createRouteMatcher(["/", "/sign-in(.*)", "/sign-up(.*)"]);
6 |
7 | const isAdminRoute = createRouteMatcher(["/admin(.*)"]);
8 |
9 | export default clerkMiddleware(async (auth, req) => {
10 | const { userId, redirectToSignIn } = await auth();
11 |
12 | if (
13 | isAdminRoute(req) &&
14 | (await auth()).sessionClaims?.metadata?.role !== "admin"
15 | ) {
16 | const url = new URL("/", req.url);
17 | return NextResponse.redirect(url);
18 | }
19 |
20 | if (!userId && !isPublicRoute(req)) {
21 | // Add custom logic to run before redirecting
22 |
23 | return redirectToSignIn();
24 | }
25 | });
26 |
27 | export const config = {
28 | matcher: [
29 | // Skip Next.js internals and all static files, unless found in search params
30 | "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
31 | // Always run for API routes
32 | "/(api|trpc)(.*)",
33 | ],
34 | };
35 |
--------------------------------------------------------------------------------
/authentication-demo/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | export default {
4 | content: [
5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | colors: {
12 | background: "var(--background)",
13 | foreground: "var(--foreground)",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | } satisfies Config;
19 |
--------------------------------------------------------------------------------
/authentication-demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/authentication-demo/types/globals.d.ts:
--------------------------------------------------------------------------------
1 | export {};
2 |
3 | // Create a type for the roles
4 | export type Roles = "admin" | "moderator";
5 |
6 | declare global {
7 | interface CustomJwtSessionClaims {
8 | metadata: {
9 | role?: Roles;
10 | };
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/core-web-vitals", "next/typescript"]
3 | }
4 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for committing if needed)
33 | .env*
34 |
35 | # vercel
36 | .vercel
37 |
38 | # typescript
39 | *.tsbuildinfo
40 | next-env.d.ts
41 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
37 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "products": [
3 | {
4 | "id": 1,
5 | "title": "Product 1",
6 | "price": 800,
7 | "description": "Updated Description 1"
8 | },
9 | {
10 | "id": 2,
11 | "title": "Product 2",
12 | "price": 1000,
13 | "description": "Description 2"
14 | },
15 | {
16 | "id": 3,
17 | "title": "Product 3",
18 | "price": 2500,
19 | "description": "Description 3"
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | };
6 |
7 | export default nextConfig;
8 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "caching-revalidation-demo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint",
10 | "serve-json": "json-server --watch db.json --port 3001"
11 | },
12 | "dependencies": {
13 | "@prisma/client": "^5.22.0",
14 | "@types/json-server": "^0.14.7",
15 | "json-server": "^0.17.4",
16 | "next": "15.0.3",
17 | "react": "19.0.0-rc-66855b96-20241106",
18 | "react-dom": "19.0.0-rc-66855b96-20241106"
19 | },
20 | "devDependencies": {
21 | "@types/node": "^20",
22 | "@types/react": "^18",
23 | "@types/react-dom": "^18",
24 | "eslint": "^8",
25 | "eslint-config-next": "15.0.3",
26 | "postcss": "^8",
27 | "prisma": "^5.22.0",
28 | "tailwindcss": "^3.4.1",
29 | "typescript": "^5"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/prisma/app.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/caching-revalidation-demo/prisma/app.db
--------------------------------------------------------------------------------
/caching-revalidation-demo/prisma/migrations/20241125081652_init/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "Product" (
3 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4 | "title" TEXT NOT NULL,
5 | "price" INTEGER NOT NULL,
6 | "description" TEXT
7 | );
8 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/prisma/migrations/migration_lock.toml:
--------------------------------------------------------------------------------
1 | # Please do not edit this file manually
2 | # It should be added in your version-control system (i.e. Git)
3 | provider = "sqlite"
--------------------------------------------------------------------------------
/caching-revalidation-demo/prisma/schema.prisma:
--------------------------------------------------------------------------------
1 | // This is your Prisma schema file,
2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema
3 |
4 | generator client {
5 | provider = "prisma-client-js"
6 | }
7 |
8 | datasource db {
9 | provider = "sqlite"
10 | url = "file:app.db"
11 | }
12 |
13 | model Product {
14 | id Int @id @default(autoincrement())
15 | title String
16 | price Int
17 | description String?
18 | }
--------------------------------------------------------------------------------
/caching-revalidation-demo/public/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/public/window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/caching-revalidation-demo/src/app/favicon.ico
--------------------------------------------------------------------------------
/caching-revalidation-demo/src/app/fonts/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/caching-revalidation-demo/src/app/fonts/GeistMonoVF.woff
--------------------------------------------------------------------------------
/caching-revalidation-demo/src/app/fonts/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/caching-revalidation-demo/src/app/fonts/GeistVF.woff
--------------------------------------------------------------------------------
/caching-revalidation-demo/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --background: #ffffff;
7 | --foreground: #171717;
8 | }
9 |
10 | @media (prefers-color-scheme: dark) {
11 | :root {
12 | --background: #0a0a0a;
13 | --foreground: #ededed;
14 | }
15 | }
16 |
17 | body {
18 | color: var(--foreground);
19 | background: var(--background);
20 | font-family: Arial, Helvetica, sans-serif;
21 | }
22 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/src/app/json-server-products/page.tsx:
--------------------------------------------------------------------------------
1 | type Product = {
2 | id: number;
3 | title: string;
4 | price: number;
5 | description: string | null;
6 | };
7 |
8 | export default async function JSONServerProductsPage() {
9 | const response = await fetch("http://localhost:3001/products", {
10 | cache: "force-cache",
11 | });
12 | const products: Product[] = await response.json();
13 | return (
14 |
15 | {products.map((product) => (
16 | -
20 |
{product.title}
21 | {product.description}
22 | ${product.price}
23 |
24 | ))}
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import localFont from "next/font/local";
3 | import "./globals.css";
4 |
5 | const geistSans = localFont({
6 | src: "./fonts/GeistVF.woff",
7 | variable: "--font-geist-sans",
8 | weight: "100 900",
9 | });
10 | const geistMono = localFont({
11 | src: "./fonts/GeistMonoVF.woff",
12 | variable: "--font-geist-mono",
13 | weight: "100 900",
14 | });
15 |
16 | export const metadata: Metadata = {
17 | title: "Create Next App",
18 | description: "Generated by create next app",
19 | };
20 |
21 | export default function RootLayout({
22 | children,
23 | }: Readonly<{
24 | children: React.ReactNode;
25 | }>) {
26 | return (
27 |
28 |
31 | {children}
32 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 |
3 | export default function Home() {
4 | return (
5 |
6 |
7 |
15 |
16 | -
17 | Get started by editing{" "}
18 |
19 | src/app/page.tsx
20 |
21 | .
22 |
23 | - Save and see your changes instantly.
24 |
25 |
26 |
51 |
52 |
99 |
100 | );
101 | }
102 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/src/app/prisma-products/page.tsx:
--------------------------------------------------------------------------------
1 | import { getProducts } from "@/prisma-db";
2 | import { unstable_cache } from "next/cache";
3 |
4 | type Product = {
5 | id: number;
6 | title: string;
7 | price: number;
8 | description: string | null;
9 | };
10 |
11 | const getProductsWithCache = unstable_cache(getProducts);
12 |
13 | export default async function PrismaProductsPage() {
14 | const products: Product[] = await getProductsWithCache();
15 | return (
16 |
17 | {products.map((product) => (
18 | -
22 |
{product.title}
23 | {product.description}
24 | ${product.price}
25 |
26 | ))}
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/src/prisma-db.ts:
--------------------------------------------------------------------------------
1 | import { PrismaClient } from "@prisma/client";
2 | const prisma = new PrismaClient();
3 |
4 | const seedProducts = async () => {
5 | const count = await prisma.product.count();
6 | if (count === 0) {
7 | await prisma.product.createMany({
8 | data: [
9 | { title: "Product 1", price: 500, description: "Description 1" },
10 | { title: "Product 2", price: 700, description: "Description 2" },
11 | { title: "Product 3", price: 1000, description: "Description 3" },
12 | ],
13 | });
14 | }
15 | };
16 |
17 | // Run seed if needed
18 | seedProducts();
19 |
20 | export async function getProducts() {
21 | await new Promise((resolve) => setTimeout(resolve, 1500));
22 | return prisma.product.findMany();
23 | }
24 |
25 | export async function getProduct(id: number) {
26 | await new Promise((resolve) => setTimeout(resolve, 1500));
27 | return prisma.product.findUnique({
28 | where: { id },
29 | });
30 | }
31 |
32 | export async function addProduct(
33 | title: string,
34 | price: number,
35 | description: string
36 | ) {
37 | await new Promise((resolve) => setTimeout(resolve, 1500));
38 | return prisma.product.create({
39 | data: { title, price, description },
40 | });
41 | }
42 |
43 | export async function updateProduct(
44 | id: number,
45 | title: string,
46 | price: number,
47 | description: string
48 | ) {
49 | await new Promise((resolve) => setTimeout(resolve, 1500));
50 | return prisma.product.update({
51 | where: { id },
52 | data: { title, price, description },
53 | });
54 | }
55 |
56 | export async function deleteProduct(id: number) {
57 | await new Promise((resolve) => setTimeout(resolve, 1500));
58 | return prisma.product.delete({
59 | where: { id },
60 | });
61 | }
62 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | export default {
4 | content: [
5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | colors: {
12 | background: "var(--background)",
13 | foreground: "var(--foreground)",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | } satisfies Config;
19 |
--------------------------------------------------------------------------------
/caching-revalidation-demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/data-fetching-demo/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/core-web-vitals", "next/typescript"]
3 | }
4 |
--------------------------------------------------------------------------------
/data-fetching-demo/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for committing if needed)
33 | .env*
34 |
35 | # vercel
36 | .vercel
37 |
38 | # typescript
39 | *.tsbuildinfo
40 | next-env.d.ts
41 |
42 | # database
43 | /prisma/app.db
44 |
--------------------------------------------------------------------------------
/data-fetching-demo/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
37 |
--------------------------------------------------------------------------------
/data-fetching-demo/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | };
6 |
7 | export default nextConfig;
8 |
--------------------------------------------------------------------------------
/data-fetching-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "data-fetching-demo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@prisma/client": "^5.22.0",
13 | "next": "15.0.3",
14 | "react": "19.0.0-rc-66855b96-20241106",
15 | "react-dom": "19.0.0-rc-66855b96-20241106"
16 | },
17 | "devDependencies": {
18 | "@types/node": "^20",
19 | "@types/react": "^18",
20 | "@types/react-dom": "^18",
21 | "eslint": "^8",
22 | "eslint-config-next": "15.0.3",
23 | "postcss": "^8",
24 | "prisma": "^5.22.0",
25 | "tailwindcss": "^3.4.1",
26 | "typescript": "^5"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/data-fetching-demo/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/data-fetching-demo/prisma/migrations/20241119043535_init/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "Product" (
3 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4 | "title" TEXT NOT NULL,
5 | "price" INTEGER NOT NULL,
6 | "description" TEXT
7 | );
8 |
--------------------------------------------------------------------------------
/data-fetching-demo/prisma/migrations/migration_lock.toml:
--------------------------------------------------------------------------------
1 | # Please do not edit this file manually
2 | # It should be added in your version-control system (i.e. Git)
3 | provider = "sqlite"
--------------------------------------------------------------------------------
/data-fetching-demo/prisma/schema.prisma:
--------------------------------------------------------------------------------
1 | // This is your Prisma schema file,
2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema
3 |
4 | generator client {
5 | provider = "prisma-client-js"
6 | }
7 |
8 | datasource db {
9 | provider = "sqlite"
10 | url = "file:app.db"
11 | }
12 |
13 | model Product {
14 | id Int @id @default(autoincrement())
15 | title String
16 | price Int
17 | description String?
18 | }
--------------------------------------------------------------------------------
/data-fetching-demo/public/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data-fetching-demo/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data-fetching-demo/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data-fetching-demo/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data-fetching-demo/public/window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/actions/products.ts:
--------------------------------------------------------------------------------
1 | "use server";
2 |
3 | import { addProduct, updateProduct, deleteProduct } from "@/prisma-db";
4 | import { redirect } from "next/navigation";
5 | import { revalidatePath } from "next/cache";
6 |
7 | export type Errors = {
8 | title?: string;
9 | price?: string;
10 | description?: string;
11 | };
12 |
13 | export type FormState = {
14 | errors: Errors;
15 | };
16 |
17 | export async function createProduct(prevState: FormState, formData: FormData) {
18 | const title = formData.get("title") as string;
19 | const price = formData.get("price") as string;
20 | const description = formData.get("description") as string;
21 |
22 | const errors: Errors = {};
23 |
24 | if (!title) {
25 | errors.title = "Title is required";
26 | }
27 |
28 | if (!price) {
29 | errors.price = "Price is required";
30 | }
31 |
32 | if (!description) {
33 | errors.description = "Description is required";
34 | }
35 |
36 | if (Object.keys(errors).length > 0) {
37 | return { errors };
38 | }
39 |
40 | await addProduct(title, parseInt(price), description);
41 | redirect("/products-db");
42 | }
43 |
44 | export async function editProduct(
45 | id: number,
46 | prevState: FormState,
47 | formData: FormData
48 | ) {
49 | const title = formData.get("title") as string;
50 | const price = formData.get("price") as string;
51 | const description = formData.get("description") as string;
52 |
53 | const errors: Errors = {};
54 |
55 | if (!title) {
56 | errors.title = "Title is required";
57 | }
58 |
59 | if (!price) {
60 | errors.price = "Price is required";
61 | }
62 |
63 | if (!description) {
64 | errors.description = "Description is required";
65 | }
66 |
67 | if (Object.keys(errors).length > 0) {
68 | return { errors };
69 | }
70 |
71 | await updateProduct(id, title, parseInt(price), description);
72 | redirect("/products-db");
73 | }
74 |
75 | export async function removeProduct(id: number) {
76 | await deleteProduct(id);
77 | revalidatePath("/products-db");
78 | }
79 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/data-fetching-demo/src/app/favicon.ico
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/fonts/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/data-fetching-demo/src/app/fonts/GeistMonoVF.woff
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/fonts/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/data-fetching-demo/src/app/fonts/GeistVF.woff
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --background: #ffffff;
7 | --foreground: #171717;
8 | }
9 |
10 | @media (prefers-color-scheme: dark) {
11 | :root {
12 | --background: #0a0a0a;
13 | --foreground: #ededed;
14 | }
15 | }
16 |
17 | body {
18 | color: var(--foreground);
19 | background: var(--background);
20 | font-family: Arial, Helvetica, sans-serif;
21 | }
22 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import localFont from "next/font/local";
3 | import "./globals.css";
4 |
5 | const geistSans = localFont({
6 | src: "./fonts/GeistVF.woff",
7 | variable: "--font-geist-sans",
8 | weight: "100 900",
9 | });
10 | const geistMono = localFont({
11 | src: "./fonts/GeistMonoVF.woff",
12 | variable: "--font-geist-mono",
13 | weight: "100 900",
14 | });
15 |
16 | export const metadata: Metadata = {
17 | title: "Create Next App",
18 | description: "Generated by create next app",
19 | };
20 |
21 | export default function RootLayout({
22 | children,
23 | }: Readonly<{
24 | children: React.ReactNode;
25 | }>) {
26 | return (
27 |
28 |
31 | {children}
32 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import Search from "@/components/search";
3 | export default function Home() {
4 | return (
5 |
6 |
7 |
8 |
16 |
17 | -
18 | Get started by editing{" "}
19 |
20 | src/app/page.tsx
21 |
22 | .
23 |
24 | - Save and see your changes instantly.
25 |
26 |
27 |
52 |
53 |
100 |
101 | );
102 | }
103 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/posts-sequential/author.tsx:
--------------------------------------------------------------------------------
1 | type User = {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | };
7 |
8 | export async function Author({ userId }: { userId: number }) {
9 | await new Promise((resolve) => setTimeout(resolve, 1000));
10 | const response = await fetch(
11 | `https://jsonplaceholder.typicode.com/users/${userId}`
12 | );
13 | const user: User = await response.json();
14 |
15 | return (
16 |
17 | Written by:{" "}
18 |
19 | {user.name}
20 |
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/posts-sequential/page.tsx:
--------------------------------------------------------------------------------
1 | import { Suspense } from "react";
2 | import { Author } from "./author";
3 |
4 | type Post = {
5 | userId: number;
6 | id: number;
7 | title: string;
8 | body: string;
9 | };
10 |
11 | export default async function PostsPage() {
12 | const response = await fetch("https://jsonplaceholder.typicode.com/posts");
13 | const posts: Post[] = await response.json();
14 |
15 | const filteredPosts = posts.filter((post) => post.id % 10 === 1);
16 |
17 | return (
18 |
19 |
Blog Posts
20 |
21 | {filteredPosts.map((post) => (
22 |
23 |
24 | {post.title}
25 |
26 |
{post.body}
27 |
Loading author...
30 | }
31 | >
32 |
33 |
34 |
35 | ))}
36 |
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/products-db-create/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { FormState, createProduct } from "@/actions/products";
4 | import { Submit } from "@/components/submit";
5 | import { useActionState } from "react";
6 |
7 | export default function AddProductPage() {
8 | const initialState: FormState = {
9 | errors: {},
10 | };
11 |
12 | const [state, formAction] = useActionState(createProduct, initialState);
13 |
14 | return (
15 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/products-db/[id]/page.tsx:
--------------------------------------------------------------------------------
1 | import { getProduct } from "@/prisma-db";
2 | import ProductEditForm from "./product-edit-form";
3 | import type { Product } from "@/app/products-db/page";
4 | import { notFound } from "next/navigation";
5 |
6 | export default async function EditProductPage({
7 | params,
8 | }: {
9 | params: Promise<{ id: string }>;
10 | }) {
11 | const { id } = await params;
12 | const product: Product | null = await getProduct(parseInt(id));
13 |
14 | if (!product) {
15 | notFound();
16 | }
17 |
18 | return ;
19 | }
20 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/products-db/[id]/product-edit-form.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { FormState, editProduct } from "@/actions/products";
4 | import { Submit } from "@/components/submit";
5 | import type { Product } from "@/app/products-db/page";
6 | import { useActionState } from "react";
7 |
8 | export default function ProductEditForm({ product }: { product: Product }) {
9 | const initialState: FormState = {
10 | errors: {},
11 | };
12 |
13 | const editProductWithId = editProduct.bind(null, product.id);
14 |
15 | const [state, formAction] = useActionState(editProductWithId, initialState);
16 |
17 | return (
18 |
69 | );
70 | }
71 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/products-db/page.tsx:
--------------------------------------------------------------------------------
1 | import { getProducts } from "@/prisma-db";
2 | import { ProductDetail } from "./product-detail";
3 |
4 | export type Product = {
5 | id: number;
6 | title: string;
7 | price: number;
8 | description: string | null;
9 | };
10 |
11 | export default async function ProductsPrismaDBPage({
12 | searchParams,
13 | }: {
14 | searchParams: Promise<{ query?: string }>;
15 | }) {
16 | const { query } = await searchParams;
17 | const products: Product[] = await getProducts(query);
18 |
19 | return ;
20 | }
21 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/products-db/product-detail.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useOptimistic } from "react";
4 | import { removeProduct } from "@/actions/products";
5 | import Link from "next/link";
6 | import Form from "next/form";
7 |
8 | export type Product = {
9 | id: number;
10 | title: string;
11 | price: number;
12 | description: string | null;
13 | };
14 |
15 | export const ProductDetail = ({ products }: { products: Product[] }) => {
16 | const [optimisticProducts, setOptimisticProducts] = useOptimistic(
17 | products,
18 | (currentProducts, productId) => {
19 | return currentProducts.filter((product) => product.id !== productId);
20 | }
21 | );
22 |
23 | const removeProductById = async (productId: number) => {
24 | setOptimisticProducts(productId);
25 | await removeProduct(productId);
26 | };
27 |
28 | return (
29 |
30 | {optimisticProducts.map((product) => (
31 | -
35 |
36 | {product.title}
37 |
38 | {product.description}
39 | ${product.price}
40 |
48 |
49 | ))}
50 |
51 | );
52 | };
53 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/react-form/api/route.ts:
--------------------------------------------------------------------------------
1 | import { addProduct } from "@/prisma-db";
2 |
3 | export async function POST(request: Request) {
4 | const body = await request.json();
5 | const { title, price, description } = body;
6 | const product = await addProduct(title, parseInt(price), description);
7 | return new Response(JSON.stringify(product), {
8 | headers: { "Content-Type": "application/json" },
9 | });
10 | }
11 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/react-form/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useRouter } from "next/navigation";
4 | import { useState } from "react";
5 |
6 | export default function CreateProduct() {
7 | const [title, setTitle] = useState("");
8 | const [price, setPrice] = useState("");
9 | const [description, setDescription] = useState("");
10 | const [loading, setLoading] = useState(false);
11 |
12 | const router = useRouter();
13 |
14 | const handleSubmit = async (e: React.FormEvent) => {
15 | e.preventDefault();
16 | setLoading(true);
17 | try {
18 | const response = await fetch("/react-form/api", {
19 | method: "POST",
20 | headers: { "Content-Type": "application/json" },
21 | body: JSON.stringify({ title, price, description }),
22 | });
23 | if (response.ok) {
24 | router.push("/products-db");
25 | }
26 | } catch (error) {
27 | console.error("Error:", error);
28 | } finally {
29 | setLoading(false);
30 | }
31 | };
32 |
33 | return (
34 |
69 | );
70 | }
71 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/user-parallel/[userId]/loading.tsx:
--------------------------------------------------------------------------------
1 | export default function LoadingPage() {
2 | return (
3 |
6 | );
7 | }
8 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/user-parallel/[userId]/page.tsx:
--------------------------------------------------------------------------------
1 | type Post = {
2 | userId: number;
3 | id: number;
4 | title: string;
5 | body: string;
6 | };
7 |
8 | type Album = {
9 | userId: number;
10 | id: number;
11 | title: string;
12 | };
13 |
14 | async function getUserPosts(userId: string) {
15 | await new Promise((resolve) => setTimeout(resolve, 1000));
16 | const res = await fetch(
17 | `https://jsonplaceholder.typicode.com/posts?userId=${userId}`
18 | );
19 | return res.json();
20 | }
21 |
22 | async function getUserAlbums(userId: string) {
23 | await new Promise((resolve) => setTimeout(resolve, 1000));
24 | const res = await fetch(
25 | `https://jsonplaceholder.typicode.com/albums?userId=${userId}`
26 | );
27 | return res.json();
28 | }
29 |
30 | export default async function Page({
31 | params,
32 | }: {
33 | params: Promise<{ userId: string }>;
34 | }) {
35 | const { userId } = await params;
36 |
37 | const postsData = getUserPosts(userId);
38 | const albumsData = getUserAlbums(userId);
39 |
40 | const [posts, albums] = await Promise.all([postsData, albumsData]);
41 | return (
42 |
43 |
User Profile
44 |
45 |
46 |
Posts
47 |
48 | {posts.map((post: Post) => (
49 |
50 |
51 | {post.title}
52 |
53 |
54 | {post.body}
55 |
56 |
57 | ))}
58 |
59 |
60 |
61 |
62 |
Albums
63 |
64 | {albums.map((album: Album) => (
65 |
68 | ))}
69 |
70 |
71 |
72 |
73 | );
74 | }
75 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/users-client/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState, useEffect } from "react";
4 |
5 | type User = {
6 | id: number;
7 | name: string;
8 | username: string;
9 | email: string;
10 | phone: string;
11 | };
12 |
13 | export default function UsersClient() {
14 | const [users, setUsers] = useState([]);
15 | const [loading, setLoading] = useState(true);
16 | const [error, setError] = useState("");
17 |
18 | useEffect(() => {
19 | async function fetchUsers() {
20 | try {
21 | const response = await fetch(
22 | "https://jsonplaceholder.typicode.com/users"
23 | );
24 | if (!response.ok) throw new Error("Failed to fetch users");
25 | const data = await response.json();
26 | setUsers(data);
27 | } catch (err) {
28 | if (err instanceof Error) {
29 | setError(err.message);
30 | } else {
31 | setError("An unknown error occurred");
32 | }
33 | } finally {
34 | setLoading(false);
35 | }
36 | }
37 | fetchUsers();
38 | }, []);
39 |
40 | if (loading) return Loading...
;
41 | if (error) return {error}
;
42 |
43 | return (
44 |
45 | {users.map((user) => (
46 | -
50 |
{user.name}
51 |
52 |
Username: {user.username}
53 |
Email: {user.email}
54 |
Phone: {user.phone}
55 |
56 |
57 | ))}
58 |
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/users-server/error.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useEffect } from "react";
3 |
4 | export default function ErrorPage({ error }: { error: Error }) {
5 | useEffect(() => {
6 | console.error(`${error}`);
7 | }, [error]);
8 | return (
9 |
10 |
Error fetching users data
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/users-server/loading.tsx:
--------------------------------------------------------------------------------
1 | export default function LoadingPage() {
2 | return (
3 |
6 | );
7 | }
8 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/app/users-server/page.tsx:
--------------------------------------------------------------------------------
1 | type User = {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | phone: string;
7 | };
8 |
9 | export default async function UsersServer() {
10 | await new Promise((resolve) => setTimeout(resolve, 2000));
11 |
12 | const response = await fetch("https://jsonplaceholder.typicode.com/users");
13 | const users: User[] = await response.json();
14 | console.log(users);
15 |
16 | return (
17 |
18 | {users.map((user) => (
19 | -
23 |
{user.name}
24 |
25 |
Username: {user.username}
26 |
Email: {user.email}
27 |
Phone: {user.phone}
28 |
29 |
30 | ))}
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/components/search.tsx:
--------------------------------------------------------------------------------
1 | import Form from "next/form";
2 |
3 | export default function Page() {
4 | return (
5 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/components/submit.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useFormStatus } from "react-dom";
3 |
4 | export const Submit = () => {
5 | const { pending } = useFormStatus();
6 | return (
7 |
14 | );
15 | };
16 |
--------------------------------------------------------------------------------
/data-fetching-demo/src/prisma-db.ts:
--------------------------------------------------------------------------------
1 | import { PrismaClient } from "@prisma/client";
2 | const prisma = new PrismaClient();
3 |
4 | const seedProducts = async () => {
5 | const count = await prisma.product.count();
6 | if (count === 0) {
7 | await prisma.product.createMany({
8 | data: [
9 | { title: "Product 1", price: 500, description: "Description 1" },
10 | { title: "Product 2", price: 700, description: "Description 2" },
11 | { title: "Product 3", price: 1000, description: "Description 3" },
12 | ],
13 | });
14 | }
15 | };
16 |
17 | // Run seed if needed
18 | seedProducts();
19 |
20 | export async function getProducts(query?: string) {
21 | await new Promise((resolve) => setTimeout(resolve, 1500));
22 | if (query) {
23 | return prisma.product.findMany({
24 | where: {
25 | OR: [
26 | { title: { contains: query } },
27 | { description: { contains: query } },
28 | ],
29 | },
30 | });
31 | }
32 | return prisma.product.findMany();
33 | }
34 |
35 | export async function getProduct(id: number) {
36 | await new Promise((resolve) => setTimeout(resolve, 1500));
37 | return prisma.product.findUnique({
38 | where: { id },
39 | });
40 | }
41 |
42 | export async function addProduct(
43 | title: string,
44 | price: number,
45 | description: string
46 | ) {
47 | await new Promise((resolve) => setTimeout(resolve, 1500));
48 | return prisma.product.create({
49 | data: { title, price, description },
50 | });
51 | }
52 |
53 | export async function updateProduct(
54 | id: number,
55 | title: string,
56 | price: number,
57 | description: string
58 | ) {
59 | await new Promise((resolve) => setTimeout(resolve, 1500));
60 | return prisma.product.update({
61 | where: { id },
62 | data: { title, price, description },
63 | });
64 | }
65 |
66 | export async function deleteProduct(id: number) {
67 | await new Promise((resolve) => setTimeout(resolve, 1500));
68 | return prisma.product.delete({
69 | where: { id },
70 | });
71 | }
72 |
--------------------------------------------------------------------------------
/data-fetching-demo/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | export default {
4 | content: [
5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | colors: {
12 | background: "var(--background)",
13 | foreground: "var(--foreground)",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | } satisfies Config;
19 |
--------------------------------------------------------------------------------
/data-fetching-demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": [
26 | "next-env.d.ts",
27 | "**/*.ts",
28 | "**/*.tsx",
29 | ".next/types/**/*.ts",
30 | "src/db.js"
31 | ],
32 | "exclude": ["node_modules"]
33 | }
34 |
--------------------------------------------------------------------------------
/hello-world/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/core-web-vitals", "next/typescript"]
3 | }
4 |
--------------------------------------------------------------------------------
/hello-world/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for committing if needed)
33 | .env*
34 |
35 | # vercel
36 | .vercel
37 |
38 | # typescript
39 | *.tsbuildinfo
40 | next-env.d.ts
41 |
--------------------------------------------------------------------------------
/hello-world/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
37 |
--------------------------------------------------------------------------------
/hello-world/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | };
6 |
7 | export default nextConfig;
8 |
--------------------------------------------------------------------------------
/hello-world/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hello-world",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "react": "19.0.0-rc-66855b96-20241106",
13 | "react-dom": "19.0.0-rc-66855b96-20241106",
14 | "next": "15.0.3"
15 | },
16 | "devDependencies": {
17 | "typescript": "^5",
18 | "@types/node": "^20",
19 | "@types/react": "^18",
20 | "@types/react-dom": "^18",
21 | "postcss": "^8",
22 | "tailwindcss": "^3.4.1",
23 | "eslint": "^8",
24 | "eslint-config-next": "15.0.3"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/hello-world/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/hello-world/public/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/hello-world/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/hello-world/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/hello-world/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/hello-world/public/window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/hello-world/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/hello-world/src/app/favicon.ico
--------------------------------------------------------------------------------
/hello-world/src/app/fonts/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/hello-world/src/app/fonts/GeistMonoVF.woff
--------------------------------------------------------------------------------
/hello-world/src/app/fonts/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/hello-world/src/app/fonts/GeistVF.woff
--------------------------------------------------------------------------------
/hello-world/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --background: #ffffff;
7 | --foreground: #171717;
8 | }
9 |
10 | @media (prefers-color-scheme: dark) {
11 | :root {
12 | --background: #0a0a0a;
13 | --foreground: #ededed;
14 | }
15 | }
16 |
17 | body {
18 | color: var(--foreground);
19 | background: var(--background);
20 | font-family: Arial, Helvetica, sans-serif;
21 | }
22 |
--------------------------------------------------------------------------------
/hello-world/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import localFont from "next/font/local";
3 | import "./globals.css";
4 |
5 | const geistSans = localFont({
6 | src: "./fonts/GeistVF.woff",
7 | variable: "--font-geist-sans",
8 | weight: "100 900",
9 | });
10 | const geistMono = localFont({
11 | src: "./fonts/GeistMonoVF.woff",
12 | variable: "--font-geist-mono",
13 | weight: "100 900",
14 | });
15 |
16 | export const metadata: Metadata = {
17 | title: "Create Next App",
18 | description: "Generated by create next app",
19 | };
20 |
21 | export default function RootLayout({
22 | children,
23 | }: Readonly<{
24 | children: React.ReactNode;
25 | }>) {
26 | return (
27 |
28 |
31 | {children}
32 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/hello-world/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 |
3 | export default function Home() {
4 | return (
5 |
6 |
7 |
15 |
16 | -
17 | Get started by editing{" "}
18 |
19 | src/app/page.tsx
20 |
21 | .
22 |
23 | - Hello world
24 |
25 |
26 |
51 |
52 |
99 |
100 | );
101 | }
102 |
--------------------------------------------------------------------------------
/hello-world/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | export default {
4 | content: [
5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | colors: {
12 | background: "var(--background)",
13 | foreground: "var(--foreground)",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | } satisfies Config;
19 |
--------------------------------------------------------------------------------
/hello-world/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/misc-demo/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/core-web-vitals", "next/typescript"]
3 | }
4 |
--------------------------------------------------------------------------------
/misc-demo/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for committing if needed)
33 | .env*
34 |
35 | # vercel
36 | .vercel
37 |
38 | # typescript
39 | *.tsbuildinfo
40 | next-env.d.ts
41 |
--------------------------------------------------------------------------------
/misc-demo/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
37 |
--------------------------------------------------------------------------------
/misc-demo/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | };
6 |
7 | export default nextConfig;
8 |
--------------------------------------------------------------------------------
/misc-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "misc-demo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "next": "15.0.3",
13 | "react": "19.0.0-rc-66855b96-20241106",
14 | "react-dom": "19.0.0-rc-66855b96-20241106"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20",
18 | "@types/react": "^18",
19 | "@types/react-dom": "^18",
20 | "eslint": "^8",
21 | "eslint-config-next": "15.0.3",
22 | "postcss": "^8",
23 | "sass": "^1.81.0",
24 | "tailwindcss": "^3.4.1",
25 | "typescript": "^5"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/misc-demo/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/misc-demo/public/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc-demo/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc-demo/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc-demo/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc-demo/public/window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc-demo/src/app/about/about.module.css:
--------------------------------------------------------------------------------
1 | .highlight {
2 | color: lightgreen;
3 | }
4 |
--------------------------------------------------------------------------------
/misc-demo/src/app/about/about.module.scss:
--------------------------------------------------------------------------------
1 | @use "../colors" as colors;
2 |
3 | .highlightscss {
4 | color: colors.$primary;
5 | }
6 |
--------------------------------------------------------------------------------
/misc-demo/src/app/about/page.tsx:
--------------------------------------------------------------------------------
1 | import styles from "./about.module.scss";
2 |
3 | export default function AboutPage() {
4 | return About Page
;
5 | }
6 |
--------------------------------------------------------------------------------
/misc-demo/src/app/colors.scss:
--------------------------------------------------------------------------------
1 | $primary: lightyellow;
2 | $secondary: lightpink;
3 |
--------------------------------------------------------------------------------
/misc-demo/src/app/contact/contact.module.css:
--------------------------------------------------------------------------------
1 | .highlight {
2 | color: lightskyblue;
3 | }
4 |
--------------------------------------------------------------------------------
/misc-demo/src/app/contact/contact.module.scss:
--------------------------------------------------------------------------------
1 | @use "../colors" as colors;
2 |
3 | .highlightscss {
4 | color: colors.$secondary;
5 | }
6 |
--------------------------------------------------------------------------------
/misc-demo/src/app/contact/page.tsx:
--------------------------------------------------------------------------------
1 | import styles from "./contact.module.scss";
2 |
3 | export default function ContactPage() {
4 | return Contact Page
;
5 | }
6 |
--------------------------------------------------------------------------------
/misc-demo/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/misc-demo/src/app/favicon.ico
--------------------------------------------------------------------------------
/misc-demo/src/app/fonts/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/misc-demo/src/app/fonts/GeistMonoVF.woff
--------------------------------------------------------------------------------
/misc-demo/src/app/fonts/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/misc-demo/src/app/fonts/GeistVF.woff
--------------------------------------------------------------------------------
/misc-demo/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --background: #ffffff;
7 | --foreground: #171717;
8 | }
9 |
10 | @media (prefers-color-scheme: dark) {
11 | :root {
12 | --background: #0a0a0a;
13 | --foreground: #ededed;
14 | }
15 | }
16 |
17 | body {
18 | color: var(--foreground);
19 | background: var(--background);
20 | font-family: Arial, Helvetica, sans-serif;
21 | }
22 |
23 | h2 {
24 | color: orange;
25 | }
26 |
--------------------------------------------------------------------------------
/misc-demo/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import localFont from "next/font/local";
3 | import "./globals.css";
4 |
5 | const geistSans = localFont({
6 | src: "./fonts/GeistVF.woff",
7 | variable: "--font-geist-sans",
8 | weight: "100 900",
9 | });
10 | const geistMono = localFont({
11 | src: "./fonts/GeistMonoVF.woff",
12 | variable: "--font-geist-mono",
13 | weight: "100 900",
14 | });
15 |
16 | export const metadata: Metadata = {
17 | title: "Create Next App",
18 | description: "Generated by create next app",
19 | };
20 |
21 | export default function RootLayout({
22 | children,
23 | }: Readonly<{
24 | children: React.ReactNode;
25 | }>) {
26 | return (
27 |
28 |
31 | {children}
32 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/misc-demo/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 |
3 | export default function Home() {
4 | return (
5 |
6 |
7 |
15 |
16 | -
17 | Get started by editing{" "}
18 |
19 | src/app/page.tsx
20 |
21 | .
22 |
23 | - Save and see your changes instantly.
24 |
25 |
26 |
51 |
52 |
99 |
100 | );
101 | }
102 |
--------------------------------------------------------------------------------
/misc-demo/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | export default {
4 | content: [
5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | colors: {
12 | background: "var(--background)",
13 | foreground: "var(--foreground)",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | } satisfies Config;
19 |
--------------------------------------------------------------------------------
/misc-demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/rendering-demo/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/core-web-vitals", "next/typescript"]
3 | }
4 |
--------------------------------------------------------------------------------
/rendering-demo/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for committing if needed)
33 | .env*
34 |
35 | # vercel
36 | .vercel
37 |
38 | # typescript
39 | *.tsbuildinfo
40 | next-env.d.ts
41 |
--------------------------------------------------------------------------------
/rendering-demo/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
37 |
--------------------------------------------------------------------------------
/rendering-demo/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | };
6 |
7 | export default nextConfig;
8 |
--------------------------------------------------------------------------------
/rendering-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rendering-demo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@types/react-slick": "^0.23.13",
13 | "next": "15.0.3",
14 | "react": "19.0.0-rc-66855b96-20241106",
15 | "react-dom": "19.0.0-rc-66855b96-20241106",
16 | "react-slick": "^0.30.2",
17 | "server-only": "^0.0.1",
18 | "slick-carousel": "^1.8.1"
19 | },
20 | "devDependencies": {
21 | "@types/node": "^20",
22 | "@types/react": "^18",
23 | "@types/react-dom": "^18",
24 | "eslint": "^8",
25 | "eslint-config-next": "15.0.3",
26 | "postcss": "^8",
27 | "tailwindcss": "^3.4.1",
28 | "typescript": "^5"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/rendering-demo/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/rendering-demo/public/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/rendering-demo/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/rendering-demo/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/rendering-demo/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/rendering-demo/public/window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/rendering-demo/src/app/about/page.tsx:
--------------------------------------------------------------------------------
1 | import { cookies } from "next/headers";
2 |
3 | export default async function AboutPage() {
4 | const cookieStore = await cookies();
5 | const theme = cookieStore.get("theme");
6 | console.log(theme);
7 | console.log("About server component");
8 | return About page {new Date().toLocaleTimeString()}
;
9 | }
10 |
--------------------------------------------------------------------------------
/rendering-demo/src/app/client-route/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React from "react";
4 | // import Slider from "react-slick";
5 | // import "slick-carousel/slick/slick.css";
6 | // import "slick-carousel/slick/slick-theme.css";
7 | import { useTheme } from "@/components/theme-provider";
8 |
9 | export default function ClientRoutePage() {
10 | const theme = useTheme();
11 | // const settings = {
12 | // dots: true,
13 | // };
14 | return (
15 |
16 |
Client route page
17 |
18 | //
19 | //
20 | //
21 | //

22 | //
23 | //
24 | //

25 | //
26 | //
27 | //

28 | //
29 | //
30 | //

31 | //
32 | //
33 | //
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/rendering-demo/src/app/dashboard/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState } from "react";
4 |
5 | export default function DashboardPage() {
6 | console.log("Dashboard client component");
7 | const [name, setName] = useState("");
8 |
9 | return (
10 |
11 |
Dashboard
12 |
setName(e.target.value)} />
13 |
Hello, {name}!
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/rendering-demo/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/rendering-demo/src/app/favicon.ico
--------------------------------------------------------------------------------
/rendering-demo/src/app/fonts/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/rendering-demo/src/app/fonts/GeistMonoVF.woff
--------------------------------------------------------------------------------
/rendering-demo/src/app/fonts/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/rendering-demo/src/app/fonts/GeistVF.woff
--------------------------------------------------------------------------------
/rendering-demo/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --background: #ffffff;
7 | --foreground: #171717;
8 | }
9 |
10 | @media (prefers-color-scheme: dark) {
11 | :root {
12 | --background: #0a0a0a;
13 | --foreground: #ededed;
14 | }
15 | }
16 |
17 | body {
18 | color: var(--foreground);
19 | background: var(--background);
20 | font-family: Arial, Helvetica, sans-serif;
21 | }
22 |
23 | .image-slider-container {
24 | margin: 0 auto;
25 | width: 400px;
26 | }
27 |
28 | .image-slider-container .slick-prev:before,
29 | .image-slider-container .slick-next:before {
30 | color: white;
31 | }
32 |
--------------------------------------------------------------------------------
/rendering-demo/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import localFont from "next/font/local";
3 | import { ThemeProvider } from "@/components/theme-provider";
4 |
5 | import "./globals.css";
6 |
7 | const geistSans = localFont({
8 | src: "./fonts/GeistVF.woff",
9 | variable: "--font-geist-sans",
10 | weight: "100 900",
11 | });
12 | const geistMono = localFont({
13 | src: "./fonts/GeistMonoVF.woff",
14 | variable: "--font-geist-mono",
15 | weight: "100 900",
16 | });
17 |
18 | export const metadata: Metadata = {
19 | title: "Create Next App",
20 | description: "Generated by create next app",
21 | };
22 |
23 | export default function RootLayout({
24 | children,
25 | }: Readonly<{
26 | children: React.ReactNode;
27 | }>) {
28 | return (
29 |
30 |
31 |
34 | {children}
35 |
36 |
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/rendering-demo/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import Link from "next/link";
3 | export default function Home() {
4 | return (
5 |
6 |
7 |
15 |
16 | -
17 | Get started by editing{" "}
18 |
19 | src/app/page.tsx
20 |
21 | .
22 |
23 | - Save and see your changes instantly.
24 |
25 |
26 |
53 |
54 |
101 |
102 | );
103 | }
104 |
--------------------------------------------------------------------------------
/rendering-demo/src/app/product-reviews/page.tsx:
--------------------------------------------------------------------------------
1 | import { Suspense } from "react";
2 |
3 | import { Product } from "@/components/product";
4 | import { Reviews } from "@/components/reviews";
5 |
6 | export default function ProductDetailPage() {
7 | return (
8 |
9 |
Product detail page
10 |
Loading product details...}>
11 |
12 |
13 |
Loading reviews...}>
14 |
15 |
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/rendering-demo/src/app/products/[id]/page.tsx:
--------------------------------------------------------------------------------
1 | export async function generateStaticParams() {
2 | return [{ id: "1" }, { id: "2" }, { id: "3" }];
3 | }
4 |
5 | export default async function ProductPage({
6 | params,
7 | }: {
8 | params: Promise<{ id: string }>;
9 | }) {
10 | const { id } = await params;
11 | return Product {id} details
;
12 | }
13 |
--------------------------------------------------------------------------------
/rendering-demo/src/app/products/page.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | export default function ProductsPage() {
4 | return (
5 | <>
6 | Products page
7 | Product 1
8 | Product 2
9 | Product 3
10 | >
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/rendering-demo/src/app/server-route/page.tsx:
--------------------------------------------------------------------------------
1 | import ImageSlider from "@/components/ImageSlider";
2 | import { serverSideFunction } from "@/utils/server-utils";
3 |
4 | export default function ServerRoutePage() {
5 | const result = serverSideFunction();
6 | return (
7 | <>
8 | Server Route {result}
9 |
10 | >
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/rendering-demo/src/components/ImageSlider.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React from "react";
4 | import Slider from "react-slick";
5 | import "slick-carousel/slick/slick.css";
6 | import "slick-carousel/slick/slick-theme.css";
7 |
8 | export default function ServerRoutePage() {
9 | const settings = {
10 | dots: true,
11 | };
12 | return (
13 |
14 |
15 |
16 |

17 |
18 |
19 |

20 |
21 |
22 |

23 |
24 |
25 |

26 |
27 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/rendering-demo/src/components/product.tsx:
--------------------------------------------------------------------------------
1 | export const Product = async () => {
2 | await new Promise((resolve) => setTimeout(resolve, 2000));
3 | return Product
;
4 | };
5 |
--------------------------------------------------------------------------------
/rendering-demo/src/components/reviews.tsx:
--------------------------------------------------------------------------------
1 | export const Reviews = async () => {
2 | await new Promise((resolve) => setTimeout(resolve, 4000));
3 | return Reviews
;
4 | };
5 |
--------------------------------------------------------------------------------
/rendering-demo/src/components/theme-provider.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { createContext, useContext } from "react";
3 |
4 | type Theme = {
5 | colors: {
6 | primary: string;
7 | secondary: string;
8 | };
9 | };
10 |
11 | const defaultTheme: Theme = {
12 | colors: {
13 | primary: "#007bff",
14 | secondary: "#6c757d",
15 | },
16 | };
17 |
18 | const ThemeContext = createContext(defaultTheme);
19 |
20 | export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
21 | return (
22 |
23 | {children}
24 |
25 | );
26 | };
27 |
28 | export const useTheme = () => useContext(ThemeContext);
29 |
--------------------------------------------------------------------------------
/rendering-demo/src/utils/server-utils.ts:
--------------------------------------------------------------------------------
1 | import "server-only";
2 |
3 | export const serverSideFunction = () => {
4 | console.log(
5 | `use multiple libraries,
6 | use environment variables,
7 | interact with a database,
8 | process confidential information`
9 | );
10 | return "server result";
11 | };
12 |
--------------------------------------------------------------------------------
/rendering-demo/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | export default {
4 | content: [
5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | colors: {
12 | background: "var(--background)",
13 | foreground: "var(--foreground)",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | } satisfies Config;
19 |
--------------------------------------------------------------------------------
/rendering-demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/route-handlers-demo/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/core-web-vitals", "next/typescript"]
3 | }
4 |
--------------------------------------------------------------------------------
/route-handlers-demo/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for committing if needed)
33 | .env*
34 |
35 | # vercel
36 | .vercel
37 |
38 | # typescript
39 | *.tsbuildinfo
40 | next-env.d.ts
41 |
--------------------------------------------------------------------------------
/route-handlers-demo/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
37 |
--------------------------------------------------------------------------------
/route-handlers-demo/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | };
6 |
7 | export default nextConfig;
8 |
--------------------------------------------------------------------------------
/route-handlers-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "route-handlers-demo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "react": "19.0.0-rc-66855b96-20241106",
13 | "react-dom": "19.0.0-rc-66855b96-20241106",
14 | "next": "15.0.3"
15 | },
16 | "devDependencies": {
17 | "typescript": "^5",
18 | "@types/node": "^20",
19 | "@types/react": "^18",
20 | "@types/react-dom": "^18",
21 | "postcss": "^8",
22 | "tailwindcss": "^3.4.1",
23 | "eslint": "^8",
24 | "eslint-config-next": "15.0.3"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/route-handlers-demo/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/route-handlers-demo/public/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/route-handlers-demo/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/route-handlers-demo/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/route-handlers-demo/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/route-handlers-demo/public/window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/api/v1/users/route.ts:
--------------------------------------------------------------------------------
1 | import { redirect } from "next/navigation";
2 |
3 | export async function GET() {
4 | redirect("/api/v2/users");
5 | }
6 |
7 | // // Original v1 endpoint - basic user info
8 | // type UserV1 = {
9 | // id: string;
10 | // email: string;
11 | // fullName: string; // Single name field
12 | // createdAt: string;
13 | // };
14 |
15 | // export async function GET() {
16 | // const users: UserV1[] = [
17 | // {
18 | // id: "550e8400-e29b-41d4-a716-446655440000",
19 | // email: "john@example.com",
20 | // fullName: "John Smith",
21 | // createdAt: "2024-01-15T08:30:00Z",
22 | // },
23 | // {
24 | // id: "7c9e6679-7425-40de-944b-e07fc1f90ae7",
25 | // email: "jane@example.com",
26 | // fullName: "Jane Wilson",
27 | // createdAt: "2024-02-20T14:15:00Z",
28 | // },
29 | // ];
30 |
31 | // return Response.json(users);
32 | // }
33 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/api/v2/users/route.ts:
--------------------------------------------------------------------------------
1 | // Enhanced v2 endpoint with structured name
2 | type UserV2 = {
3 | // v1 fields maintained
4 | id: string;
5 | email: string;
6 | fullName: string; // Kept for backwards compatibility
7 | createdAt: string;
8 |
9 | // new v2 fields
10 | name: {
11 | // Structured name object
12 | first: string;
13 | last: string;
14 | middle?: string; // Optional middle name
15 | };
16 | status: "active" | "inactive" | "suspended";
17 | lastLoginAt: string | null;
18 | profile: {
19 | avatar: string | null;
20 | title: string | null;
21 | department: string | null;
22 | };
23 | preferences: {
24 | language: string;
25 | timezone: string;
26 | emailNotifications: boolean;
27 | };
28 | };
29 |
30 | export async function GET() {
31 | const users: UserV2[] = [
32 | {
33 | // v1 fields
34 | id: "550e8400-e29b-41d4-a716-446655440000",
35 | email: "john@example.com",
36 | fullName: "John Smith", // Maintained for v1 clients
37 | createdAt: "2024-01-15T08:30:00Z",
38 | // v2 fields
39 | name: {
40 | // New structured format
41 | first: "John",
42 | last: "Smith",
43 | },
44 | status: "active",
45 | lastLoginAt: "2024-03-15T09:20:00Z",
46 | profile: {
47 | avatar: "https://assets.example.com/avatars/john.jpg",
48 | title: "Senior Developer",
49 | department: "Engineering",
50 | },
51 | preferences: {
52 | language: "en-US",
53 | timezone: "America/New_York",
54 | emailNotifications: true,
55 | },
56 | },
57 | {
58 | id: "7c9e6679-7425-40de-944b-e07fc1f90ae7",
59 | email: "jane@example.com",
60 | fullName: "Jane Wilson",
61 | createdAt: "2024-02-20T14:15:00Z",
62 | name: {
63 | first: "Jane",
64 | last: "Wilson",
65 | middle: "Elizabeth", // Example with middle name
66 | },
67 | status: "active",
68 | lastLoginAt: "2024-03-14T16:45:00Z",
69 | profile: {
70 | avatar: null,
71 | title: "Product Manager",
72 | department: "Product",
73 | },
74 | preferences: {
75 | language: "en-GB",
76 | timezone: "Europe/London",
77 | emailNotifications: false,
78 | },
79 | },
80 | ];
81 |
82 | return Response.json(users);
83 | }
84 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/categories/route.ts:
--------------------------------------------------------------------------------
1 | export const dynamic = "force-static";
2 |
3 | export async function GET() {
4 | // This data would typically come from a database
5 | const categories = [
6 | { id: 1, name: "Electronics" },
7 | { id: 2, name: "Books" },
8 | { id: 3, name: "Clothing" },
9 | { id: 4, name: "Home & Garden" },
10 | ];
11 |
12 | return Response.json(categories);
13 | }
14 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/comments/[id]/route.ts:
--------------------------------------------------------------------------------
1 | import { comments } from "../data";
2 |
3 | export async function GET(
4 | _request: Request,
5 | { params }: { params: Promise<{ id: string }> }
6 | ) {
7 | const { id } = await params;
8 | const comment = comments.find((comment) => comment.id === parseInt(id));
9 | return Response.json(comment);
10 | }
11 |
12 | export async function PATCH(
13 | request: Request,
14 | { params }: { params: Promise<{ id: string }> }
15 | ) {
16 | const { id } = await params;
17 | const body = await request.json();
18 | const { text } = body;
19 |
20 | const index = comments.findIndex((comment) => comment.id === parseInt(id));
21 | comments[index].text = text;
22 | return Response.json(comments[index]);
23 | }
24 |
25 | export async function DELETE(
26 | _request: Request,
27 | { params }: { params: Promise<{ id: string }> }
28 | ) {
29 | const { id } = await params;
30 | const index = comments.findIndex((comment) => comment.id === parseInt(id));
31 | const deletedComment = comments[index];
32 | comments.splice(index, 1);
33 | return Response.json(deletedComment);
34 | }
35 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/comments/data.ts:
--------------------------------------------------------------------------------
1 | export const comments = [
2 | {
3 | id: 1,
4 | text: "This is the first comment",
5 | },
6 | {
7 | id: 2,
8 | text: "This is the second comment",
9 | },
10 | {
11 | id: 3,
12 | text: "This is the third comment",
13 | },
14 | ];
15 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/comments/route.ts:
--------------------------------------------------------------------------------
1 | import { NextRequest } from "next/server";
2 | import { comments } from "./data";
3 |
4 | // export async function GET() {
5 | // return Response.json(comments);
6 | // }
7 |
8 | export async function GET(request: NextRequest) {
9 | const searchParams = request.nextUrl.searchParams;
10 | const query = searchParams.get("query");
11 | const filteredComments = query
12 | ? comments.filter((comment) => comment.text.includes(query))
13 | : comments;
14 | return Response.json(filteredComments);
15 | }
16 |
17 | export async function POST(request: Request) {
18 | const comment = await request.json();
19 | const newComment = { id: comments.length + 1, text: comment.text };
20 | comments.push(newComment);
21 | return new Response(JSON.stringify(newComment), {
22 | headers: { "Content-Type": "application/json" },
23 | status: 201,
24 | });
25 | }
26 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/dashboard/route.ts:
--------------------------------------------------------------------------------
1 | export async function GET() {
2 | return new Response("Dashboard data");
3 | }
4 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/dashboard/users/route.ts:
--------------------------------------------------------------------------------
1 | export async function GET() {
2 | return new Response("Users data");
3 | }
4 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/route-handlers-demo/src/app/favicon.ico
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/fonts/GeistMonoVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/route-handlers-demo/src/app/fonts/GeistMonoVF.woff
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/fonts/GeistVF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/route-handlers-demo/src/app/fonts/GeistVF.woff
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --background: #ffffff;
7 | --foreground: #171717;
8 | }
9 |
10 | @media (prefers-color-scheme: dark) {
11 | :root {
12 | --background: #0a0a0a;
13 | --foreground: #ededed;
14 | }
15 | }
16 |
17 | body {
18 | color: var(--foreground);
19 | background: var(--background);
20 | font-family: Arial, Helvetica, sans-serif;
21 | }
22 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/hello/route.ts:
--------------------------------------------------------------------------------
1 | export async function GET() {
2 | return new Response("Hello world!");
3 | }
4 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import localFont from "next/font/local";
3 | import "./globals.css";
4 |
5 | const geistSans = localFont({
6 | src: "./fonts/GeistVF.woff",
7 | variable: "--font-geist-sans",
8 | weight: "100 900",
9 | });
10 | const geistMono = localFont({
11 | src: "./fonts/GeistMonoVF.woff",
12 | variable: "--font-geist-mono",
13 | weight: "100 900",
14 | });
15 |
16 | export const metadata: Metadata = {
17 | title: "Create Next App",
18 | description: "Generated by create next app",
19 | };
20 |
21 | export default function RootLayout({
22 | children,
23 | }: Readonly<{
24 | children: React.ReactNode;
25 | }>) {
26 | return (
27 |
28 |
31 | {children}
32 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 |
3 | export default function Home() {
4 | return (
5 |
6 |
7 |
15 |
16 | -
17 | Get started by editing{" "}
18 |
19 | src/app/page.tsx
20 |
21 | .
22 |
23 | - Save and see your changes instantly.
24 |
25 |
26 |
51 |
52 |
99 |
100 | );
101 | }
102 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/profile/api/route.ts:
--------------------------------------------------------------------------------
1 | import { type NextRequest } from "next/server";
2 | import { headers } from "next/headers";
3 | import { cookies } from "next/headers";
4 |
5 | export async function GET(request: NextRequest) {
6 | const requestHeaders = new Headers(request.headers);
7 | console.log(requestHeaders.get("Authorization"));
8 |
9 | const headersList = await headers();
10 | console.log(headersList.get("Authorization"));
11 |
12 | const theme = request.cookies.get("theme");
13 | console.log(theme);
14 |
15 | const cookieStore = await cookies();
16 | cookieStore.set("resultsPerPage", "20");
17 | console.log(cookieStore.get("resultsPerPage"));
18 |
19 | return new Response("Profile API data
", {
20 | headers: {
21 | "Content-Type": "text/html",
22 | "Set-Cookie": `theme=dark`,
23 | },
24 | });
25 | }
26 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/profile/page.tsx:
--------------------------------------------------------------------------------
1 | export default function ProfilePage() {
2 | return Profile Page
;
3 | }
4 |
--------------------------------------------------------------------------------
/route-handlers-demo/src/app/time/route.ts:
--------------------------------------------------------------------------------
1 | export const dynamic = "force-static";
2 | export const revalidate = 10;
3 |
4 | export async function GET() {
5 | return Response.json({ time: new Date().toLocaleTimeString() });
6 | }
7 |
--------------------------------------------------------------------------------
/route-handlers-demo/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | export default {
4 | content: [
5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | colors: {
12 | background: "var(--background)",
13 | foreground: "var(--foreground)",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | } satisfies Config;
19 |
--------------------------------------------------------------------------------
/route-handlers-demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/routing-demo/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/core-web-vitals", "next/typescript"]
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for committing if needed)
33 | .env*
34 |
35 | # vercel
36 | .vercel
37 |
38 | # typescript
39 | *.tsbuildinfo
40 | next-env.d.ts
41 |
--------------------------------------------------------------------------------
/routing-demo/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
37 |
--------------------------------------------------------------------------------
/routing-demo/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | };
6 |
7 | export default nextConfig;
8 |
--------------------------------------------------------------------------------
/routing-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "routing-demo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "react": "19.0.0-rc-66855b96-20241106",
13 | "react-dom": "19.0.0-rc-66855b96-20241106",
14 | "next": "15.0.3"
15 | },
16 | "devDependencies": {
17 | "typescript": "^5",
18 | "@types/node": "^20",
19 | "@types/react": "^18",
20 | "@types/react-dom": "^18",
21 | "postcss": "^8",
22 | "tailwindcss": "^3.4.1",
23 | "eslint": "^8",
24 | "eslint-config-next": "15.0.3"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/routing-demo/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/routing-demo/public/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/routing-demo/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/routing-demo/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/routing-demo/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/routing-demo/public/window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/routing-demo/src/app/(auth)/forgot-password/page.tsx:
--------------------------------------------------------------------------------
1 | export default function ForgotPassword() {
2 | return Forgot Password
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/(auth)/login/page.tsx:
--------------------------------------------------------------------------------
1 | export default function Login() {
2 | return Login
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/(auth)/register/page.tsx:
--------------------------------------------------------------------------------
1 | export default function Register() {
2 | return Register
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/(auth)/styles.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/(auth)/template.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState } from "react";
4 | import Link from "next/link";
5 | import { usePathname } from "next/navigation";
6 | import "./styles.css";
7 |
8 | const navLinks = [
9 | { name: "Register", href: "/register" },
10 | { name: "Login", href: "/login" },
11 | { name: "Forgot Password", href: "/forgot-password" },
12 | ];
13 |
14 | export default function AuthLayout({
15 | children,
16 | }: {
17 | children: React.ReactNode;
18 | }) {
19 | const pathname = usePathname();
20 | const [input, setInput] = useState("");
21 | return (
22 |
23 |
24 | setInput(e.target.value)} />
25 |
26 | {navLinks.map((link) => {
27 | const isActive =
28 | pathname === link.href ||
29 | (pathname.startsWith(link.href) && link.href !== "/");
30 |
31 | return (
32 |
37 | {link.name}
38 |
39 | );
40 | })}
41 | {children}
42 |
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/routing-demo/src/app/_lib/format-date.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/routing-demo/src/app/_lib/format-date.js
--------------------------------------------------------------------------------
/routing-demo/src/app/_lib/page.tsx:
--------------------------------------------------------------------------------
1 | export default function PrivateRoute() {
2 | return You cannot view this in the browser
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/about/page.tsx:
--------------------------------------------------------------------------------
1 | export const metadata = {
2 | title: "About Codevolution",
3 | };
4 |
5 | export default function About() {
6 | return About me
;
7 | }
8 |
--------------------------------------------------------------------------------
/routing-demo/src/app/articles/[articleId]/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import Link from "next/link";
3 | import { use } from "react";
4 |
5 | export default function NewsArticle({
6 | params,
7 | searchParams,
8 | }: {
9 | params: Promise<{ articleId: string }>;
10 | searchParams: Promise<{ lang?: "en" | "es" | "fr" }>;
11 | }) {
12 | const { articleId } = use(params);
13 | const { lang = "en" } = use(searchParams);
14 |
15 | return (
16 |
17 |
News Article #{articleId}
18 |
Reading in: {lang.toUpperCase()}
19 |
20 | {/* Language switcher */}
21 |
22 | English |
23 | Español |
24 | Français
25 |
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/routing-demo/src/app/blog/first/page.tsx:
--------------------------------------------------------------------------------
1 | export default function FirstBlog() {
2 | return First blog post
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/blog/page.tsx:
--------------------------------------------------------------------------------
1 | import { Metadata } from "next";
2 |
3 | export const metadata: Metadata = {
4 | title: {
5 | absolute: "blog",
6 | },
7 | };
8 | export default function Blog() {
9 | return My blog
;
10 | }
11 |
--------------------------------------------------------------------------------
/routing-demo/src/app/blog/second/page.tsx:
--------------------------------------------------------------------------------
1 | export default function SecondBlog() {
2 | return Second blog post
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/complex-dashboard/@login/page.tsx:
--------------------------------------------------------------------------------
1 | import { Card } from "@/components/card";
2 |
3 | export default function Login() {
4 | return Login;
5 | }
6 |
--------------------------------------------------------------------------------
/routing-demo/src/app/complex-dashboard/@notifications/archived/page.tsx:
--------------------------------------------------------------------------------
1 | import { Card } from "@/components/card";
2 | import Link from "next/link";
3 |
4 | export default function ArchivedNotifications() {
5 | return (
6 |
7 | Archived notifications
8 |
9 | Default
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/routing-demo/src/app/complex-dashboard/@notifications/page.tsx:
--------------------------------------------------------------------------------
1 | import { Card } from "@/components/card";
2 | import Link from "next/link";
3 |
4 | export default function Notifications() {
5 | return (
6 |
7 | Notifications
8 |
9 | Archived
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/routing-demo/src/app/complex-dashboard/@revenue/default.tsx:
--------------------------------------------------------------------------------
1 | import { Card } from "@/components/card";
2 |
3 | export default function RevenueMetrics() {
4 | return Default Revenue metrics;
5 | }
6 |
--------------------------------------------------------------------------------
/routing-demo/src/app/complex-dashboard/@revenue/page.tsx:
--------------------------------------------------------------------------------
1 | import { Card } from "@/components/card";
2 |
3 | export default function RevenueMetrics() {
4 | return Revenue Metrics;
5 | }
6 |
--------------------------------------------------------------------------------
/routing-demo/src/app/complex-dashboard/@users/default.tsx:
--------------------------------------------------------------------------------
1 | import { Card } from "@/components/card";
2 |
3 | export default function UsersAnalytics() {
4 | return Default Users analytics;
5 | }
6 |
--------------------------------------------------------------------------------
/routing-demo/src/app/complex-dashboard/@users/page.tsx:
--------------------------------------------------------------------------------
1 | import { Card } from "@/components/card";
2 |
3 | export default function UsersAnalytics() {
4 | return User Analytics;
5 | }
6 |
--------------------------------------------------------------------------------
/routing-demo/src/app/complex-dashboard/default.tsx:
--------------------------------------------------------------------------------
1 | export default function ComplexDashboardPage() {
2 | return Default Complex dashboard
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/complex-dashboard/layout.tsx:
--------------------------------------------------------------------------------
1 | export default function ComplexDashboardLayout({
2 | children,
3 | users,
4 | revenue,
5 | notifications,
6 | login,
7 | }: {
8 | children: React.ReactNode;
9 | users: React.ReactNode;
10 | revenue: React.ReactNode;
11 | notifications: React.ReactNode;
12 | login: React.ReactNode;
13 | }) {
14 | const isLoggedIn = true;
15 | return isLoggedIn ? (
16 |
17 |
{children}
18 |
19 |
20 |
{users}
21 |
{revenue}
22 |
23 |
{notifications}
24 |
25 |
26 | ) : (
27 | login
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/routing-demo/src/app/complex-dashboard/page.tsx:
--------------------------------------------------------------------------------
1 | export default function ComplexDashboardPage() {
2 | return Complex dashboard
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/counter/counter.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useState } from "react";
3 |
4 | export const Counter = () => {
5 | const [count, setCount] = useState(0);
6 | return (
7 |
8 |
Count: {count}
9 |
10 |
11 | );
12 | };
13 |
--------------------------------------------------------------------------------
/routing-demo/src/app/counter/page.tsx:
--------------------------------------------------------------------------------
1 | import { Counter } from "./counter";
2 |
3 | export const metadata = {
4 | title: "Counter",
5 | };
6 |
7 | export default function CounterPage() {
8 | return ;
9 | }
10 |
--------------------------------------------------------------------------------
/routing-demo/src/app/dashboard/line-chart.tsx:
--------------------------------------------------------------------------------
1 | export default function LineChart() {
2 | return Line chart
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/dashboard/page.tsx:
--------------------------------------------------------------------------------
1 | // function BarChart() {
2 | // return Bar chart
;
3 | // }
4 |
5 | export default function Dashboard() {
6 | return Dashboard
;
7 | }
8 |
--------------------------------------------------------------------------------
/routing-demo/src/app/docs/[[...slug]]/page.tsx:
--------------------------------------------------------------------------------
1 | export default async function Doc({
2 | params,
3 | }: {
4 | params: Promise<{ slug: string[] }>;
5 | }) {
6 | const { slug } = await params;
7 | if (slug?.length === 2) {
8 | return (
9 |
10 | Viewing docs for feature {slug[0]} and concept {slug[1]}
11 |
12 | );
13 | } else if (slug?.length === 1) {
14 | return Viewing docs for feature {slug[0]}
;
15 | }
16 | return Docs home page
;
17 | }
18 |
--------------------------------------------------------------------------------
/routing-demo/src/app/error-wrapper.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import "./globals.css";
3 |
4 | import { useState } from "react";
5 |
6 | interface WrapperProps {
7 | children: React.ReactNode;
8 | }
9 |
10 | const ErrorSimulator = ({
11 | message = "An error occurred",
12 | }: {
13 | message?: string;
14 | }) => {
15 | const [error, setError] = useState(false);
16 |
17 | if (error) throw new Error(message);
18 |
19 | return (
20 |
27 | );
28 | };
29 |
30 | export const ErrorWrapper = ({ children }: WrapperProps) => {
31 | return (
32 |
33 |
34 |
35 |
36 | {children}
37 |
38 | );
39 | };
40 |
--------------------------------------------------------------------------------
/routing-demo/src/app/f1/(.)f2/page.tsx:
--------------------------------------------------------------------------------
1 | export default function InterceptedF2() {
2 | return (.) Intercepted F2 page
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/f1/(..)f3/page.tsx:
--------------------------------------------------------------------------------
1 | export default function InterceptedF3() {
2 | return (..) Intercepted F3 page
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/f1/f2/(..)(..)f4/page.tsx:
--------------------------------------------------------------------------------
1 | export default function InterceptedF4() {
2 | return (..)(..) Intercepted F4 page
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/f1/f2/inner-f2/(...)f5/page.tsx:
--------------------------------------------------------------------------------
1 | export default function InterceptedF5() {
2 | return (...) Intercepted F5 page
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/f1/f2/inner-f2/page.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | export default function InnerF2() {
4 | return (
5 | <>
6 | Inner F2 page
7 |
8 | F5
9 |
10 | >
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/routing-demo/src/app/f1/f2/page.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | export default function F2() {
4 | return (
5 | <>
6 | F2 page
7 |
8 | F4
9 |
10 | >
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/routing-demo/src/app/f1/page.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | export default function F1() {
4 | return (
5 | <>
6 | F1 page
7 |
8 | F2
9 | F3
10 |
11 | >
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/routing-demo/src/app/f3/page.tsx:
--------------------------------------------------------------------------------
1 | export default function F3() {
2 | return F3 page
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/f4/page.tsx:
--------------------------------------------------------------------------------
1 | export default function F4() {
2 | return F4 page
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/f5/page.tsx:
--------------------------------------------------------------------------------
1 | export default function F5() {
2 | return F5 page
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/global-error.tsx:
--------------------------------------------------------------------------------
1 | "use client"; // Error boundaries must be Client Components
2 |
3 | import "./globals.css";
4 |
5 | export default function GlobalError() {
6 | return (
7 |
8 |
9 |
10 |
Something went wrong!
11 |
20 |
21 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/routing-demo/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import { Metadata } from "next";
2 | // import { ErrorWrapper } from "./error-wrapper";
3 |
4 | export const metadata: Metadata = {
5 | title: {
6 | default: "Next.js Tutorial - Codevolution",
7 | template: "%s | Codevolution",
8 | },
9 | description: "Generated by Next.js",
10 | };
11 |
12 | export default function RootLayout({
13 | children,
14 | }: {
15 | children: React.ReactNode;
16 | }) {
17 | return (
18 |
19 |
20 |
28 | {/* */}
29 | {children}
30 | {/* */}
31 |
39 |
40 |
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/routing-demo/src/app/not-found.tsx:
--------------------------------------------------------------------------------
1 | export default function NotFound() {
2 | return (
3 |
4 |
Page Not Found
5 |
Could not find requested resource
6 |
7 | );
8 | }
9 |
--------------------------------------------------------------------------------
/routing-demo/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | export default function Home() {
4 | return (
5 | <>
6 | Welcome home!
7 | Blog
8 | Products
9 | >
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/routing-demo/src/app/photo-feed/@modal/(.)[id]/page.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import wondersImages, { WonderImage } from "@/app/photo-feed/wonders";
3 | import Modal from "@/components/modal";
4 |
5 | export default async function PhotoModal({
6 | params,
7 | }: {
8 | params: Promise<{ id: string }>;
9 | }) {
10 | const { id } = await params;
11 | const photo: WonderImage = wondersImages.find((p) => p.id === id)!;
12 |
13 | return (
14 |
15 |
20 |
21 |
22 |
{photo.name}
23 | {photo.photographer}
24 | {photo.location}
25 |
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/routing-demo/src/app/photo-feed/@modal/default.tsx:
--------------------------------------------------------------------------------
1 | export default function Default() {
2 | return null;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/photo-feed/[id]/page.tsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import wondersImages, { WonderImage } from "../wonders";
3 |
4 | export default async function PhotoPage({
5 | params,
6 | }: {
7 | params: Promise<{ id: string }>;
8 | }) {
9 | const { id } = await params;
10 | const photo: WonderImage = wondersImages.find((p) => p.id === id)!;
11 | return (
12 |
13 |
14 |
15 |
{photo.name}
16 |
17 |
22 |
23 |
24 |
{photo.photographer}
25 | {photo.location}
26 |
27 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/routing-demo/src/app/photo-feed/layout.tsx:
--------------------------------------------------------------------------------
1 | import "./styles.css";
2 |
3 | export default function Layout(props: {
4 | modal: React.ReactNode;
5 | children: React.ReactNode;
6 | }) {
7 | return (
8 | <>
9 | {props.modal}
10 | {props.children}
11 | >
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/routing-demo/src/app/photo-feed/page.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import wonders from "./wonders";
3 | import Image from "next/image";
4 |
5 | export default function Home() {
6 | return (
7 |
8 |
9 | New Wonders of the World
10 |
11 |
12 | {wonders.map(({ id, src, name }) => (
13 |
14 |
19 |
20 | ))}
21 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/routing-demo/src/app/photo-feed/photos/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/routing-demo/src/app/photo-feed/photos/1.jpg
--------------------------------------------------------------------------------
/routing-demo/src/app/photo-feed/photos/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/routing-demo/src/app/photo-feed/photos/2.jpg
--------------------------------------------------------------------------------
/routing-demo/src/app/photo-feed/photos/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/routing-demo/src/app/photo-feed/photos/3.jpg
--------------------------------------------------------------------------------
/routing-demo/src/app/photo-feed/photos/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/routing-demo/src/app/photo-feed/photos/4.jpg
--------------------------------------------------------------------------------
/routing-demo/src/app/photo-feed/photos/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/routing-demo/src/app/photo-feed/photos/5.jpg
--------------------------------------------------------------------------------
/routing-demo/src/app/photo-feed/photos/6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/routing-demo/src/app/photo-feed/photos/6.jpg
--------------------------------------------------------------------------------
/routing-demo/src/app/photo-feed/photos/7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gopinav/Next.js-15-Tutorials/908ec36a3607355c97f2f7173e2f4e5634a1e702/routing-demo/src/app/photo-feed/photos/7.jpg
--------------------------------------------------------------------------------
/routing-demo/src/app/photo-feed/styles.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/routing-demo/src/app/photo-feed/wonders.ts:
--------------------------------------------------------------------------------
1 | import { StaticImageData } from "next/image";
2 | import photo1 from "./photos/1.jpg";
3 | import photo2 from "./photos/2.jpg";
4 | import photo3 from "./photos/3.jpg";
5 | import photo4 from "./photos/4.jpg";
6 | import photo5 from "./photos/5.jpg";
7 | import photo6 from "./photos/6.jpg";
8 | import photo7 from "./photos/7.jpg";
9 |
10 | export type WonderImage = {
11 | id: string;
12 | name: string;
13 | src: StaticImageData;
14 | photographer: string;
15 | location: string;
16 | };
17 |
18 | const wondersImages: WonderImage[] = [
19 | {
20 | id: "1",
21 | name: "Great Wall of China",
22 | src: photo1,
23 | photographer: "Photo by Max van den Oetelaar on Unsplash",
24 | location: "China",
25 | },
26 | {
27 | id: "2",
28 | name: "Petra",
29 | src: photo2,
30 | photographer: "Photo by Reiseuhu on Unsplash",
31 | location: "Jordan",
32 | },
33 | {
34 | id: "3",
35 | name: "Christ the Redeemer",
36 | src: photo3,
37 | photographer: "Photo by Andrea Leopardi on Unsplash",
38 | location: "Brazil",
39 | },
40 | {
41 | id: "4",
42 | name: "Machu Picchu",
43 | src: photo4,
44 | photographer: "Photo by Jared Schwitzke on Unsplash",
45 | location: "Peru",
46 | },
47 | {
48 | id: "5",
49 | name: "Chichen Itza",
50 | src: photo5,
51 | photographer: "Photo by E Mens on Unsplash",
52 | location: "Mexico",
53 | },
54 | {
55 | id: "6",
56 | name: "Roman Colosseum",
57 | src: photo6,
58 | photographer: "Photo by Andrea Cipriano on Unsplash",
59 | location: "Italy",
60 | },
61 | {
62 | id: "7",
63 | name: "Taj Mahal",
64 | src: photo7,
65 | photographer: "Photo by Su San Lee on Unsplash",
66 | location: "India",
67 | },
68 | ];
69 |
70 | export default wondersImages;
71 |
--------------------------------------------------------------------------------
/routing-demo/src/app/products/[productId]/layout.tsx:
--------------------------------------------------------------------------------
1 | // function getRandomInt(count: number) {
2 | // return Math.floor(Math.random() * count);
3 | // }
4 |
5 | export default function ProductDetailsLayout({
6 | children,
7 | }: {
8 | children: React.ReactNode;
9 | }) {
10 | // const random = getRandomInt(2);
11 | // if (random === 1) {
12 | // throw new Error("Error loading product");
13 | // }
14 |
15 | return (
16 | <>
17 | {children}
18 | Features products
19 | >
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/routing-demo/src/app/products/[productId]/page.tsx:
--------------------------------------------------------------------------------
1 | import { Metadata } from "next";
2 |
3 | type Props = {
4 | params: Promise<{ productId: string }>;
5 | };
6 |
7 | export const generateMetadata = async ({
8 | params,
9 | }: Props): Promise => {
10 | const productId = (await params).productId;
11 | const title = await new Promise((resolve) => {
12 | setTimeout(() => {
13 | resolve(`iPhone ${productId}`);
14 | }, 100);
15 | });
16 | return {
17 | title: `Product - ${title}`,
18 | };
19 | };
20 |
21 | export default async function ProductDetails({ params }: Props) {
22 | const productId = (await params).productId;
23 | return Details about product {productId}
;
24 | }
25 |
--------------------------------------------------------------------------------
/routing-demo/src/app/products/[productId]/reviews/[reviewId]/error.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useRouter } from "next/navigation";
4 | import { startTransition } from "react";
5 | export default function ErrorBoundary({
6 | error,
7 | reset,
8 | }: {
9 | error: Error;
10 | reset: () => void;
11 | }) {
12 | const router = useRouter();
13 | const reload = () => {
14 | startTransition(() => {
15 | router.refresh();
16 | reset();
17 | });
18 | };
19 | return (
20 |
21 | {error.message}{" "}
22 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/routing-demo/src/app/products/[productId]/reviews/[reviewId]/not-found.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { usePathname } from "next/navigation";
3 |
4 | export default function NotFound() {
5 | const pathname = usePathname();
6 | const productId = pathname.split("/")[2];
7 | const reviewId = pathname.split("/")[4];
8 | // console.log({ pathname, productId, reviewId });
9 | return (
10 |
11 |
12 | Review {reviewId} not found for product {productId}
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/routing-demo/src/app/products/[productId]/reviews/[reviewId]/page.tsx:
--------------------------------------------------------------------------------
1 | import { notFound } from "next/navigation";
2 |
3 | // function getRandomInt(count: number) {
4 | // return Math.floor(Math.random() * count);
5 | // }
6 |
7 | export default async function ProductReview({
8 | params,
9 | }: {
10 | params: Promise<{ productId: string; reviewId: string }>;
11 | }) {
12 | // const random = getRandomInt(2);
13 | // if (random === 1) {
14 | // throw new Error("Error loading review");
15 | // }
16 |
17 | const { productId, reviewId } = await params;
18 |
19 | if (parseInt(reviewId) > 1000) {
20 | notFound();
21 | }
22 | return (
23 |
24 | Review {reviewId} for product {productId}
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/routing-demo/src/app/products/error.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useRouter } from "next/navigation";
4 | import { startTransition } from "react";
5 | export default function ErrorBoundary({
6 | error,
7 | reset,
8 | }: {
9 | error: Error;
10 | reset: () => void;
11 | }) {
12 | const router = useRouter();
13 | const reload = () => {
14 | startTransition(() => {
15 | router.refresh();
16 | reset();
17 | });
18 | };
19 | return (
20 |
21 | {error.message}
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/routing-demo/src/app/products/page.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | export default function ProductList() {
4 | const productId = 100;
5 |
6 | return (
7 | <>
8 | Home
9 | Product List
10 |
11 | Product 1
12 |
13 |
14 | Product 2
15 |
16 |
17 |
18 | Product 3
19 |
20 |
21 |
22 | Product {productId}
23 |
24 | >
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/routing-demo/src/app/profile/page.tsx:
--------------------------------------------------------------------------------
1 | export default function Profile() {
2 | return My profile
;
3 | }
4 |
--------------------------------------------------------------------------------
/routing-demo/src/components/card.tsx:
--------------------------------------------------------------------------------
1 | export const Card = ({ children }: { children: React.ReactNode }) => {
2 | const cardStyle = {
3 | padding: "100px",
4 | margin: "10px",
5 | boxShadow: "0 4px 8px 0 rgba(0, 0, 0, 0.2)",
6 | border: "1px solid #ddd",
7 | display: "flex",
8 | justifyContent: "center",
9 | alignItems: "center",
10 | };
11 |
12 | return {children}
;
13 | };
14 |
--------------------------------------------------------------------------------
/routing-demo/src/components/modal.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useCallback, useRef, useEffect, MouseEventHandler } from "react";
3 | import { useRouter } from "next/navigation";
4 |
5 | export default function Modal({ children }: { children: React.ReactNode }) {
6 | const overlay = useRef(null);
7 | const wrapper = useRef(null);
8 | const router = useRouter();
9 |
10 | const onDismiss = useCallback(() => {
11 | router.back();
12 | }, [router]);
13 |
14 | const onClick: MouseEventHandler = useCallback(
15 | (e) => {
16 | if (e.target === overlay.current || e.target === wrapper.current) {
17 | if (onDismiss) onDismiss();
18 | }
19 | },
20 | [onDismiss, overlay, wrapper]
21 | );
22 |
23 | const onKeyDown = useCallback(
24 | (e: KeyboardEvent) => {
25 | if (e.key === "Escape") onDismiss();
26 | },
27 | [onDismiss]
28 | );
29 |
30 | useEffect(() => {
31 | document.addEventListener("keydown", onKeyDown);
32 | return () => document.removeEventListener("keydown", onKeyDown);
33 | }, [onKeyDown]);
34 |
35 | return (
36 |
41 |
45 | {children}
46 |
47 |
48 | );
49 | }
50 |
--------------------------------------------------------------------------------
/routing-demo/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | export default {
4 | content: [
5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | colors: {
12 | background: "var(--background)",
13 | foreground: "var(--foreground)",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | } satisfies Config;
19 |
--------------------------------------------------------------------------------
/routing-demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------