coders gyan(Next.js) ---> book library(2.1)

code

npx create-next-app@latest

what is your project name? --> elib-client-app
would you like to use Typescript? --> yes
would you like to use Eslibt? --> yes
tailwind css? --> yes
src directory? --> yes
App Router --> yes
customize the default import alias? --> no

cd elib-client-app
npm run dev

file routing →(create folder and page.tsx)

src/app/page.tsx → / → home page

src/app/about/page.tsx → /about → about page

src/app/users/contact/page.tsx → /users/contact →contact page

dynamic file routing →[]

src/app/book/[bookId]/page.tsx →/book/23123 → single book page

() → not act as file routing

Layout

app/layout.tsx → automatically wrap all pages.tsx → {children}

→ so common(repated ui is placed here) —> Nav,Footer,Header

//app/layout.tsx 
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
import Navbar from '@/components/Navbar';
import Footer from '@/components/Footer';

const inter = Inter({ subsets: ['latin'] });

export const metadata: Metadata = {
    title: 'Create Next App',
    description: 'Generated by create next app',
};

export default function RootLayout({
    children,
}: Readonly<{
    children: React.ReactNode;
}>) {
    return (
        <html lang="en">
            <body className={inter.className}>
                <Navbar />
                {children}
                <Footer />
            </body>
        </html>
    );
}
//src/components/Navbar.tsx
import Link from 'next/link';
import React from 'react';

const Navbar = () => {
    return (
        <nav className="border-b">
           // max-w-7xl or container
            <div className="max-w-7xl mx-auto flex items-center justify-between py-4">
                <div>
                    <Link href={'/'}>
                        <div className="flex items-center gap-1">
                            <div className="relative">
                                <Hexagon />
                                <BookIcon />
                            </div>
                            <span className="text-xl font-bold uppercase tracking-tight text-primary-500">
                                Coders Book
                            </span>
                        </div>
                    </Link>
                </div>
                <div className="flex items-center gap-4">
                    <button className="h-10 rounded-md border border-primary-500 px-4 py-2 text-sm font-medium text-primary-500 transition-all hover:border-primary-100 hover:bg-primary-100 active:border-primary-200 active:bg-primary-200">
                        Sign in
                    </button>
                    <button className="h-10 rounded-md bg-primary-500 px-4 py-2 text-sm font-medium text-white transition-all hover:bg-primary-600 active:bg-primary-700">
                        Sign up
                    </button>
                </div>
            </div>
        </nav>
    );
};

export default Navbar;

const Hexagon = () => (
    <svg
        xmlns="http://www.w3.org/2000/svg"
        width="45"
        height="45"
        viewBox="0 0 24 24"
        fill="#ce7041"
        stroke="#ce7041"
        strokeWidth="2"
        strokeLinecap="round"
        strokeLinejoin="round"
        className="lucide lucide-hexagon">
        <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
    </svg>
);

const BookIcon = () => (
    <svg
        xmlns="http://www.w3.org/2000/svg"
        fill="#fff"
        viewBox="0 0 24 24"
        strokeWidth={2}
        stroke="#ce7041"
        className="absolute left-1/2 top-1/2 h-8 w-8 -translate-x-1/2 -translate-y-1/2 transform">
        <path
            strokeLinecap="round"
            strokeLinejoin="round"
            d="M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.967 8.967 0 0 0-6 2.292m0-14.25v14.25"
        />
    </svg>
);
// tailwind.config.ts 
// 
import type { Config } from 'tailwindcss';

const config: Config = {
    content: [
        './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
        './src/components/**/*.{js,ts,jsx,tsx,mdx}',
        './src/app/**/*.{js,ts,jsx,tsx,mdx}',
    ],
    theme: {
        extend: {
            backgroundImage: {
                'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
                'gradient-conic':
                    'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
            },
            // primary color added 
            colors: {
                primary: {
                    50: '#fcf6f0',
                    100: '#f7eadd',
                    200: '#eed2ba',
                    300: '#e3b28e',
                    400: '#d78d60',
                    500: '#ce7041',
                    600: '#bf5a36',
                    700: '#9f462f',
                    800: '#803a2c',
                    900: '#683226',
                    950: '#381712',
                },
            },
        },
    },
    plugins: [],
};
export default config;

src/components →common or shared component for complete ui

src/app/(home)/components→ component used by home page only

//src/app/(home)/components/Banner.tsx
import React from 'react';
import Image from 'next/image';

const Banner = () => {
    return (
        <div className="mx-auto max-w-7xl py-10">
            <div className="relative">
                <Image
                    src={'/paper-bg.jpg'} // taking img from public folder
                    alt="billboard"
                    className="h-72 w-full rounded-lg"
                    height={0}
                    width={0}
                    sizes="100vw"
                />
                <div className="absolute inset-0 h-full w-full rounded-lg bg-gray-950 opacity-30" />
                <Image
                    src={'/book.png'}
                    alt="billboard"
                    className="absolute bottom-0 right-5"
                    height={0}
                    width={0}
                    sizes="100vw"
                    style={{ width: 'auto', height: '18rem' }}
                />
                <h3 className="absolute left-10 top-1/2 w-full max-w-3xl -translate-y-1/2 text-5xl font-semibold tracking-tight text-white">
                    Connect, Share and Trade Your Favourite Reads...
                </h3>
            </div>
        </div>
    );
};

export default Banner;
//src/app/(home)/components/Banner.tsx
import React from 'react';
import Image from 'next/image';

const Banner = () => {
    return (
        <div className="mx-auto max-w-7xl py-10">
            <div className="relative">
                <Image
                    src={'/paper-bg.jpg'}
                    alt="billboard"
                    className="h-72 w-full rounded-lg"
                    height={0}
                    width={0}
                    sizes="100vw"
                />
                <div className="absolute inset-0 h-full w-full rounded-lg bg-gray-950 opacity-30" />
                <Image
                    src={'/book.png'}
                    alt="billboard"
                    className="absolute bottom-0 right-5"
                    height={0}
                    width={0}
                    sizes="100vw"
                    style={{ width: 'auto', height: '18rem' }}
                />
                <h3 className="absolute left-10 top-1/2 w-full max-w-3xl -translate-y-1/2 text-5xl font-semibold tracking-tight text-white">
                    Connect, Share and Trade Your Favourite Reads...
                </h3>
            </div>
        </div>
    );
};

export default Banner;
//src/types/index.ts
export type Book = {
    _id: string;
    title: string;
    description: string;
    coverImage: string;
    file: string;
    author: Author;
};

export type Author = {
    name: string;
};
// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
    images: {
        remotePatterns: [
            {
                protocol: 'https',
                hostname: 'res.cloudinary.com',
            },
        ],
    },
};

export default nextConfig;
//src/app/(home)/components/BookCard.tsx
import { Book } from '@/types';
import Image from 'next/image';
import Link from 'next/link';
import React from 'react';

const BookCard = ({ book }: { book: Book }) => {
    return (
        <div className="flex gap-5 border p-5 shadow-md rounded">
            <Image
                src={book.coverImage}
                alt={book.title}
                width={0}
                height={0}
                sizes="100vw"
                style={{ width: 'auto', height: '12rem' }}
            />
            <div>
                <h2 className="line-clamp-2 text-xl font-bold text-primary-600 text-balance">
                    {book.title}
                </h2>
                <p className="font-bold text-primary-900 mt-1">{book.author.name}</p>
                <Link
                    href={`/book/${book._id}`}
                    className="py-1 px-2 rounded border border-primary-500 mt-4 inline-block text-primary-500 font-medium text-sm
                    hover:border-primary-100 hover:bg-primary-100 transition">
                    Read more
                </Link>
            </div>
        </div>
    );
};

export default BookCard;
//src/app/(home)/components/BookList.tsx
import React from 'react';
import BookCard from './BookCard';
import { Book } from '@/types';

const BookList = async () => {
    // data fetching -> always use fetch in next.js
    const response = await fetch(`${process.env.BACKEND_URL}/books`, { cache: 'no-store' });
    if (!response.ok) {
        throw new Error('An error occurred while fetching the books');
    }

    const books = await response.json();

    return (
        <div className="grid grid-cols-1 gap-8 md:grid-cols-3 max-w-7xl mx-auto mb-10">
            {books.map((book: Book) => (
                <BookCard key={book._id} book={book} />
            ))}
        </div>
    );
};

export default BookList;

2 types of component?

client component →data fetching , parsing data to create html file is done on client side

server component → data fetching , parsing data to create html file is done on server side → faster file load, Seo

in next.js → by default all pages and layout are server component

“use client“ → to make server component to client component

// src/app/(home)/pages.tsx
import Banner from '@/app/(home)/components/Banner';
import Image from 'next/image';
import BookList from './components/BookList';
import { Suspense } from 'react';
import Loading from '@/components/Loading';
// export const dynamic = 'force-dynamic';

export default async function Home() {
    return (
        <>
            <Banner />
            <Suspense fallback={<Loading />}>
                <BookList />
            </Suspense>
        </>
    );
}