Module 1

Frontend with Next.js

Learn how to build fast, modern user interfaces with Next.js and the App Router. We start with "what is a component?" and work up to streaming, caching and performance tuning.

Basic → Advanced TypeScript + JSX Bootstrap styling Diagrams on every topic
Level 1 — BasicsSetup, components, routing, layouts, navigation and styling

What is Next.js? Basic

React is a JavaScript library for building user interfaces out of small reusable pieces called components. On its own, React only handles the UI. It does not handle routing, servers, SEO or bundling.

Next.js is a React framework. It adds everything a real website needs on top of React: file-based routing, server rendering, an API/backend layer, image and font optimization, and a build system.

Think of it like thisReact is the engine. Next.js is the whole car: engine, wheels, steering, dashboard. You can build a car around the engine yourself, but Next.js gives you one that is ready to drive.
What Next.js adds on top of React
RoutingFolders become URLs automatically
Server rendering (SSR / SSG / ISR)HTML is built on the server → fast first load and good SEO
Backend (Route Handlers + Server Actions)Write APIs in the same project
OptimizationsImages, fonts, code-splitting, caching
React (the UI library)Components, JSX, hooks, state

How a page reaches the user

Request → response lifecycle
Usertypes example.com/blog
HTTP request
Next.js serverfinds app/blog/page.tsx
renders HTML
HTML + JSsent to browser
hydration
Interactive pagebuttons now work

Hydration means React "attaches" to the HTML the server sent, so it becomes interactive.

SEO friendly

Search engines get real HTML instead of an empty <div>.

Fast

Server rendering, code-splitting and caching are built in.

Full stack

Frontend and backend live in one project, one language.

Setup & your first project Basic

Prerequisites

  1. Install Node.js (LTS)Download it from nodejs.org. Check the install with node -v and npm -v.
  2. Install a code editorVS Code is recommended. Add the ES7+ React snippets and Prettier extensions.
  3. Know basic HTML, CSS and JavaScriptMainly variables, functions, arrays, objects, map(), arrow functions, async/await and modules.

Create the app

CommandWhat it doesWhen to use
npm run devDevelopment server with hot reloadWhile coding
npm run buildBuilds an optimized production versionBefore deploying
npm run startRuns the production buildOn the server
npm run lintChecks code for problemsBefore committing
TipRecent versions of Next.js use Turbopack, a very fast bundler written in Rust, so dev server start-up and reloads are near-instant.

Project structure Basic

In the App Router, the app/ folder is the heart of your project. Every folder inside it can become a URL segment.

A typical Next.js project
  • my-app/
    • app/ ← routes live here
      • layout.tsx ← root layout (html, body), wraps every page
      • page.tsx ← the "/" home page
      • globals.css ← global styles
      • about/
        • page.tsx ← "/about"
      • api/
        • route.ts ← backend endpoint (see Backend notes)
    • components/ ← reusable UI pieces (Navbar, Card…)
    • lib/ ← helpers, db client, utils
    • public/ ← static files served at "/" (images, favicon)
    • next.config.ts ← Next.js configuration
    • package.json ← dependencies + scripts
    • tsconfig.json ← TypeScript settings
    • .env.local ← secrets (never commit!)
Only special file names become routesA folder becomes publicly reachable only when it contains a page.tsx (UI) or route.ts (API). That means you can safely put components inside app/ too.

JSX, components & props Basic

A component is a function that returns UI. That UI is written in JSX, a syntax that looks like HTML but lives inside JavaScript.

A page is a tree of components
Page Navbar ProductList Footer ProductCard ProductCard ProductCard

Data flows down the tree through props.

JSX rules to remember

HTMLJSXWhy
class="btn"className="btn"class is a reserved word in JS
for="email"htmlFor="email"for is reserved too
onclick="..."onClick={fn}Events use camelCase and take a function
style="color:red"style={{ color: "red" }}Style is an object
<img><img />Every tag must be closed
Many root elementsOne root or <>...</>A component returns a single element (a Fragment is fine)

File-based routing Basic

In Next.js you don't write a router config. The folder structure is the router. Each folder is a URL segment, and a page.tsx file makes that segment visible.

Folder → URL mapping
app/page.tsx
becomes
/
app/about/page.tsx
becomes
/about
app/blog/[slug]/page.tsx
becomes
/blog/hello-world
app/dashboard/settings/page.tsx
becomes
/dashboard/settings

Special files in a route folder

page.tsx

The UI for that URL. Makes the route public.

layout.tsx

A shared wrapper (navbar, sidebar). It stays mounted when you navigate between pages.

loading.tsx

Shown instantly while the page loads its data.

error.tsx

Catches errors in that segment and shows a fallback UI.

not-found.tsx

A custom 404 page.

route.ts

An API endpoint (GET, POST…). Covered on the Backend page.

You created app/contact/ContactForm.tsx. Is /contact now a page?

Layouts Basic

A layout wraps pages and keeps its state when you navigate. Layouts nest: the root layout wraps the dashboard layout, which wraps the dashboard pages.

Nested layouts for /dashboard/settings
app/layout.tsx — <html>, <body>, Navbar, Footer
app/dashboard/layout.tsx — Sidebar
app/dashboard/settings/page.tsx
the only part that changes
layout vs templatelayout.tsx is kept alive between navigations, so its state survives. template.tsx has the same shape but is re-created on every navigation. Use it when you want an animation or effect to run again on each page.

Styling: CSS, CSS Modules & Bootstrap Basic

Global CSS

globals.css is imported once in the root layout and applies everywhere.

CSS Modules

Button.module.css: class names are scoped to one component, so they never clash.

Bootstrap

Ready-made grid and components. Install it with npm and import the CSS in the layout.

Adding Bootstrap to Next.js

  1. Installnpm install bootstrap bootstrap-icons
  2. Import the CSS in the root layoutimport "bootstrap/dist/css/bootstrap.min.css";
  3. Load Bootstrap's JS on the client onlyDropdowns, modals and other interactive components need JS. Bootstrap's JS uses document, so it must load in the browser.

CSS Modules example

Mixing is fineUse Bootstrap utility classes (py-5, d-flex, row) for layout, and CSS Modules for anything custom.

Images, fonts & metadata Basic

next/image

The <Image> component resizes images, lazy-loads them, serves modern formats like WebP/AVIF, and reserves space so the layout doesn't jump.

next/font

Metadata (SEO)

Level 2 — IntermediateServer vs Client components, hooks, dynamic routes, data fetching and forms

Server vs Client Components Intermediate

This is the most important concept in the App Router. Every component is a Server Component by default. You opt in to a Client Component by adding "use client" at the very top of the file.

Server Component (default)
  • Runs only on the server
  • Can be async and await data directly
  • Can read the database and secrets safely
  • Zero JS is sent to the browser for it
  • ✗ No useState, useEffect or onClick
VS
Client Component
  • Starts with "use client"
  • Pre-rendered on the server, then hydrated in the browser
  • Can use state, effects and event handlers
  • Can use browser APIs (window, localStorage)
  • ✗ Never put secrets or DB calls here
Keep "use client" at the leaves of the tree
page.tsx (S) Header (S) ProductList (S) SearchBox (C) AddToCartButton (C) S = Server · C = Client ("use client")

Only the interactive pieces ship JavaScript. Everything else stays on the server.

Rules of composition
  • A Client Component cannot import a Server Component. It can receive one as children or another prop.
  • Props passed from server to client must be serializable: strings, numbers, plain objects and arrays. Functions are not allowed, except Server Actions.
  • "use client" marks a boundary. Everything that file imports also becomes client code.

A component uses onClick. What must you add?

React hooks essentials Intermediate

Hooks are functions that start with use and let Client Components have state and side effects. Call them at the top level of a component, never inside if-statements or loops.

The state → render loop
User clicks
event
setCount(c+1)state updates
triggers
Re-rendercomponent runs again
React diffs
DOM updatedonly what changed
HookPurposeAnalogy
useStateRemember a value and re-render when it changesA whiteboard that redraws the room when edited
useEffectSync with outside systems after render"After you finish painting, turn on the lights"
useRefMutable box; access DOM nodesA sticky note that doesn't trigger a redraw
useMemo / useCallbackCache a value / a function between rendersRemembering a calculation's answer
useContextRead shared data without prop drillingSee the React Context page
useReducerComplex state with actionsA mini Redux (see the React page)
Common mistakeLeaving the dependency array out of useEffect makes the effect run after every render. If the effect also sets state, you get an infinite loop.

Dynamic routes & params Intermediate

FolderMatchesparams
app/blog/[slug]/blog/hello{ slug: "hello" }
app/shop/[...parts]/shop/men/shoes/nike{ parts: ["men","shoes","nike"] }
app/docs/[[...parts]]/docs and /docs/a/b{ parts: undefined | [...] }
In Client ComponentsUse useParams() and useSearchParams() from next/navigation. Inside a client component you can also unwrap the params Promise with React's use(params).

Loading, error & not-found UI Intermediate

Next.js wraps your page in React boundaries automatically, based on these special files:

How Next.js nests the special files
<Layout> layout.tsx
<ErrorBoundary fallback=<Error/>> error.tsx
<Suspense fallback=<Loading/>> loading.tsx
<NotFoundBoundary> not-found.tsx
<Page /> page.tsx

Data fetching Intermediate

In the App Router, the simplest approach is to fetch in a Server Component with async/await. You don't need useEffect or a loading state for this.

Sequential vs parallel fetching

Sequential (waterfall) — slow

getUser() 1s
getPosts() 1s
Total ≈ 2s

Parallel (Promise.all) — fast

getUser() 1s
getPosts() 1s
Total ≈ 1s

Fetching on the client (when needed)

Fetch in the browser for data that depends on user interaction or changes very often, such as live search or polling. Use a library like SWR or TanStack Query. They handle caching, retries and revalidation for you.

Forms & Server Actions Intermediate

A Server Action is an async function marked with "use server". You can pass it straight to a form's action. Next.js turns it into a POST request behind the scenes, so you don't have to write an API route.

Server Action flow
<form action={createTodo}>
POST (automatic)
createTodo(formData)"use server" — runs on server
save
PostgreSQL
revalidatePath
UI refreshed
SecurityA Server Action is a public endpoint. Always validate the input (for example with Zod) and check that the user is authenticated inside the action.
Level 3 — AdvancedRendering strategies, caching, streaming, advanced routing and performance

Rendering strategies Advanced

The main question is when the HTML gets built. Next.js chooses the strategy per route, based on what your code uses.

When is the HTML generated?
Build time Request time (server) In the browser SSG ISR re-built every N sec SSR CSR
StrategyHTML builtBest forHow in App Router
SSG StaticOnce, at buildMarketing pages, docs, blogsDefault when no dynamic APIs are used
ISR IncrementalAt build, then re-built in the backgroundProduct pages, newsexport const revalidate = 60
SSR DynamicOn every requestDashboards, personalised pagesUsing cookies(), headers() or searchParams, or cache: "no-store"
CSR ClientIn the browserHighly interactive widgets"use client" + SWR / useEffect
StreamingIn chunksPages with some slow parts<Suspense> / loading.tsx
Check the build outputnpm run build prints each route with a symbol: ○ Static, ● SSG with params, ƒ Dynamic. Use it to confirm which strategy each page got.

Caching & revalidation Advanced

Next.js caches work at several layers. Learn these layers and you'll understand most "why is my data stale?" bugs.

Cache layers
Client Router CacheIn the browser: visited/prefetched route segments for fast back/forward
Full Route CacheOn the server: rendered HTML + RSC payload of static routes
Data CacheOn the server: results of fetch() / "use cache" functions, persisted across requests
Request MemoizationSame fetch called twice in one render → executed once
Newer model: "use cache"Since Next.js 16 you can turn on Cache Components (cacheComponents: true in next.config.ts). Then you mark a component or function with "use cache" to cache it, and control its lifetime with cacheLife() and cacheTag(). Read the official caching docs for the version you're using, because the defaults have changed between major releases.

Streaming & Suspense Advanced

Without streaming, the slowest query blocks the whole page. With <Suspense>, the page shell appears at once and the slow parts stream in as they finish.

What the user sees over time
0 msHeader + skeletons
stream
200 msProduct info appears
stream
1500 msSlow reviews appear

Advanced routing patterns Advanced

Route groups (name)

Organize routes without changing the URL. app/(marketing)/about → /about. Each group can have its own layout.

Private folders _name

app/_components is ignored by the router, so it's a safe place for helper files.

Parallel routes @slot

Render several pages in one layout at the same time. Useful for dashboards: @analytics + @team.

Intercepting routes (.)photo

Open /photo/1 in a modal while browsing the feed. A refresh shows the full page.

Example structure
  • app/
    • (marketing)/ ← group: public site layout
      • layout.tsx
      • pricing/page.tsx → /pricing
    • (app)/ ← group: logged-in layout
      • dashboard/
        • layout.tsx ← receives {children, analytics, team}
        • @analytics/page.tsx
        • @team/page.tsx
    • feed/
      • (.)photo/[id]/page.tsx ← modal version
    • photo/[id]/page.tsx ← full-page version

Performance & SEO Advanced

LCP

Largest Contentful Paint. Should be under 2.5s. Use priority on the hero image.

INP

Interaction to Next Paint. Keep JS small and move logic to the server.

CLS

Cumulative Layout Shift. next/image and next/font prevent it.

Performance checklist

  • Keep "use client" as low in the tree as possible
  • Fetch in parallel and stream slow parts with Suspense
  • Use next/image, next/font and next/script (strategy="lazyOnload")
  • Lazy-load heavy libraries with next/dynamic
  • Cache data that doesn't change often
  • Check bundle size with @next/bundle-analyzer

Environment variables & deployment Advanced

Never put secrets in NEXT_PUBLIC_ variables. They are copied into the JavaScript that is sent to every visitor.
Deploy pipeline
git push
triggers
next buildCI / Vercel
deploys
Edge / Node serverVercel, Docker, VPS
serves
Users
OptionHowNotes
VercelImport the GitHub repoZero config. Made by the creators of Next.js.
Dockeroutput: "standalone" in next.configSmall image. Run it anywhere.
VPSnpm run build && npm start + PM2 + NginxFull control
Static exportoutput: "export"No server features (no SSR, actions or API routes)
Practice & ReviewInterview questions, exercises and a cheat sheet

Interview questions

The App Router (app/) is the modern router. It is built on React Server Components and supports nested layouts, streaming and Server Actions. The Pages Router (pages/) is the older one, with getServerSideProps and getStaticProps. New projects should use the App Router.

Hydration is when React attaches event handlers to HTML that was rendered on the server. An error happens when the server HTML differs from what the client renders. Common causes are Date.now(), Math.random(), reading window during render, and invalid HTML nesting such as a <div> inside a <p>.

When you need interactivity (onClick, onChange), state or effects (useState, useEffect), browser-only APIs, or a library that depends on them. Use a Server Component for everything else.

SSG builds the HTML once, at build time. SSR builds it on every request. ISR serves the static HTML and rebuilds it in the background after the revalidate period, so you get static speed with data that stays fairly fresh.

<Link> navigates on the client without a full page reload, prefetches routes when they come into view, and keeps shared layouts mounted.

Practice projects

  1. Portfolio siteStatic pages, a Bootstrap navbar, next/image and metadata.
  2. BlogDynamic routes [slug], generateStaticParams, a loading UI and a 404 page.
  3. Todo appServer Actions, useActionState and revalidatePath, with PostgreSQL behind it (see the Backend page).
  4. DashboardRoute groups, parallel routes, Suspense streaming and auth-protected pages.

Cheat sheet

Files
  • page.tsxroute UI
  • layout.tsxshared wrapper
  • loading.tsxsuspense fallback
  • error.tsxerror boundary
  • not-found.tsx404
  • route.tsAPI endpoint
Folders
  • [id]dynamic
  • [...all]catch-all
  • [[...all]]optional catch-all
  • (group)no URL segment
  • _privatenot a route
  • @slotparallel route
Navigation
  • <Link href>links
  • useRouter()client push/back
  • usePathname()current path
  • redirect()server redirect
  • notFound()trigger 404
Caching
  • revalidate = 60ISR
  • cache: "no-store"always fresh
  • next: { tags }tag a fetch
  • revalidatePath()purge a path
  • revalidateTag()purge a tag
Frontend notes