Why Next.js on top of React
React draws components. Next.js turns them into a product: file-system routing, server rendering, streaming, metadata, image/font pipelines, and a Node server (or static export) that can talk to Mongo and Sanity without a separate “frontend API” for first paint.
This site is Next.js 16 (App Router, Turbopack, React 19). Study materials are static — they generate on the server. No useEffect fetch, no client tabs for the outline.
App Router
Folders under app/ are routes. page.tsx is the UI. layout.tsx wraps every nested page and does not remount when you navigate inside it. loading.tsx and error.tsx are optional boundaries.
| File | Role |
|---|---|
page.tsx | The route UI. Required for a URL. |
layout.tsx | Shared chrome (nav, footer). Nested layouts compose. |
(resources)/ | A route group — organizational, not in the URL. |
generateMetadata | Title, description, canonical, OG. |
Layouts and templates
Root app/layout.tsx owns html, fonts, theme, and default metadata. Nested layouts add a shell. Do not put a use-client directive on a layout just to make a header interactive — extract StickyNavbar as a client leaf.
Server Components
Default. They can be async. They can read files, env, and databases. They cannot use state, effects, or browser APIs. Pass data down as props to a small client component.
export default async function Page() {
const notes = await getNotes(); // server
return <NoteList notes={notes} />;
}params are Promises
Next 15/16 pass params and searchParams as Promises. Type them that way and await them in the page and in generateMetadata.
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPost(slug);
return <article>{post.title}</article>;
}Data fetching and cache
In Next 16, fetch is not cached by default. State your intent every time.
// Revalidate on a timer + tag for on-demand invalidation
await fetch(url, { next: { revalidate: 1800, tags: ["posts"] } });
// Always fresh
await fetch(url, { cache: "no-store" });- After a mutation, call
revalidateTagorrevalidatePath— do not wait for the timer. - Study materials skip fetch entirely: they import TS modules. The route is static.
force-dynamicis for pages that must run per request (session-specific).
Server actions
A server action is a function marked with a use-server directive that the client can call from a form or button. Validate with Zod, mutate, then revalidate.
"use server";
import { revalidatePath } from "next/cache";
import { noteSchema } from "@/lib/schemas/note";
export async function createNote(input: unknown) {
const { title } = noteSchema.parse(input);
await db.collection("notes").insertOne({ title, createdAt: new Date() });
revalidatePath("/notes");
return { success: true };
}Metadata and SEO
Export metadata on static pages, or generateMetadata when the title depends on params. ReactBD uses generateSEOMetadata({ title, description, path, keywords }) so canonical, OG, and Twitter cards stay consistent.
export const metadata = generateSEOMetadata({
title: "Next.js — App Router, SSR, server actions",
description: "Learn Next.js the way ReactBD ships it.",
path: "/study-materials/nextjs",
keywords: ["next.js app router", "learn nextjs"],
});JSON-LD belongs in the body as a <script type="application/ld+json">, not in the Metadata object. Detail pages here emit Article.
Proxy (middleware)
Next 16 moved middleware toward a proxy entry (apps/web/proxy.ts in this repo). Use it for locale, auth gates, and rewrites — keep it fast. Do not query Mongo on every request if a cookie check will do.
Environment variables
NEXT_PUBLIC_*is inlined into the browser bundle. No secrets.- Server-only keys (
MONGODB_URI,AUTH_SECRET) stay on the server. - This monorepo layers root
.envthen.env.development/.env.production.
ReactBD conventions
- Server Components by default. Client leaves for widgets.
generateSEOMetadata+ JSON-LD on public pages.- Zod schemas shared between forms and actions.
- Sanity via
serverFetchwithrevalidate+ tags — never GROQ in the browser for lists. - Public API catalogs from Nest (
GET /api/public/tools) with featured-first UI.
Your first App Router page
- 1
Add a folder
app/(resources)/study-materials/nextjs/page.tsx→/study-materials/nextjs. - 2
Export metadata
Title, description,
pathfor the canonical URL. - 3
Render on the server
Import static data or
awaita fetch with an explicit cache policy. - 4
Client only where needed
Copy buttons, dialogs, theme toggles — not the article.