How we ship React

Next.js

App Router, Server Components, caching, server actions, and metadata — the production ReactBD defaults on Next.js 16.

App RouterSSRCachingServer ActionsMetadata

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.

FileRole
page.tsxThe route UI. Required for a URL.
layout.tsxShared chrome (nav, footer). Nested layouts compose.
(resources)/A route group — organizational, not in the URL.
generateMetadataTitle, 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.

app/notes/page.tsx
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.

app/blog/[slug]/page.tsx
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.

fetch.ts
// 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 revalidateTag or revalidatePath — do not wait for the timer.
  • Study materials skip fetch entirely: they import TS modules. The route is static.
  • force-dynamic is 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.

actions/notes.ts
"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.

page.tsx
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 .env then .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 serverFetch with revalidate + 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. 1

    Add a folder

    app/(resources)/study-materials/nextjs/page.tsx → /study-materials/nextjs.

  2. 2

    Export metadata

    Title, description, path for the canonical URL.

  3. 3

    Render on the server

    Import static data or await a fetch with an explicit cache policy.

  4. 4

    Client only where needed

    Copy buttons, dialogs, theme toggles — not the article.

Related

Part of the ReactBD MERN documentation. Static content, rendered on the server.