mrkeyoor.com_
Wed 05 Aug 05:02 UTC
npmWeb Frontendupdated 05 Aug 2026

next

Next.js is Vercel's full-stack React framework. It gives React apps the things React itself does not: file-based routing (the App Router), server-side rendering, static generation with incremental revalidation, React Server Components, server actions for mutations, API route handlers, and built-in image and font optimization. Since v16 the Rust-based Turbopack bundler is the default. In practice it is the mainstream way to ship a React site that needs SEO or server rendering.

Verdict

The safe default for React apps that need server rendering, with the largest community and hosting story in the ecosystem. Budget real time for major-version migrations and think twice if your app is a dashboard that never needed SSR in the first place.

API stability3/5Core semantics keep moving between majors: pages-to-app router, sync-to-async params in 15, fetch caching defaults flipped, middleware renamed to proxy in 16. Codemods help but upgrades are never free.
Docs5/5nextjs.org/docs is thorough, versioned, and paired with a free interactive course; the caching docs finally explain the model honestly after years of complaints.
Maintenance5/5Backed by Vercel with a full-time team; pushed the same day as this review, frequent canaries, fast security response with a bug bounty.
Ecosystem5/5Anything React works with it, every auth/CMS/analytics vendor ships a Next integration first, and deployment guides exist for every host.

Use it if

  • You are building a public-facing React site where SEO and first-paint speed matter (marketing, e-commerce, content) and you want SSR/SSG without wiring it yourself
  • You want one repo where pages, API endpoints and data mutations (server actions) live together with React Server Components cutting client JS
  • You deploy on Vercel and want ISR, image optimization and edge rendering to work with zero configuration
  • Your team already knows React and you need conventions instead of assembling a router, bundler and SSR layer from parts
Skip it if

Setup reality

npx create-next-app@latest scaffolds a working TypeScript app in a minute, and Turbopack makes dev startup genuinely fast. The pain arrives later: the caching model (what is static, what revalidates, what is dynamic) has changed defaults across majors and still confuses experienced teams; params and searchParams are now Promises you must await; middleware.ts was renamed proxy.ts in v16; some webpack-era plugins and libraries with deep bundler assumptions do not work under Turbopack; and self-hosting means understanding output standalone, a Node server, and giving up or rebuilding parts of image optimization.

Patterns

Fetch data in a Server Component pageserver-page

// app/posts/page.tsx (Server Component by default)
export default async function PostsPage() {
  const res = await fetch("https://api.example.com/posts");
  const posts = await res.json();
  return <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>;
}

Server Components can be async and never ship this code to the browser; fetch here is NOT cached by default since v15.

Dynamic segment with async paramsdynamic-route

// app/posts/[slug]/page.tsx
export default async function Post({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  return <h1>{slug}</h1>;
}

params and searchParams are Promises since v15; forgetting the await is the most common upgrade break.

JSON API endpointroute-handler

// app/api/hello/route.ts
import { NextResponse } from "next/server";

export async function GET() {
  return NextResponse.json({ ok: true });
}

export async function POST(req: Request) {
  const body = await req.json();
  return NextResponse.json({ received: body }, { status: 201 });
}

Handlers use Web-standard Request/Response; a route.ts cannot live in the same folder as a page.tsx.

Mutate data with a server actionserver-action

// app/actions.ts
"use server";
import { revalidatePath } from "next/cache";

export async function createTodo(formData: FormData) {
  await db.todo.create({ data: { title: formData.get("title") as string } });
  revalidatePath("/todos");
}

// in a component: <form action={createTodo}>...</form>

Actions run only on the server but are exposed as POST endpoints; validate input like any public API.

Interactive component with stateclient-component

"use client";
import { useState } from "react";

export function Counter() {
  const [n, setN] = useState(0);
  return <button onClick={() => setN(n + 1)}>{n}</button>;
}

The "use client" directive marks the boundary; everything it imports gets bundled for the browser, so keep these leaves small.

Pre-render dynamic pages at build timestatic-generation

// app/posts/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await fetch("https://api.example.com/posts").then((r) => r.json());
  return posts.map((p) => ({ slug: p.slug }));
}

Unlisted slugs render on demand unless you export dynamicParams = false.

Cache a fetch and revalidate on a timer or tagrevalidation

const res = await fetch("https://api.example.com/prices", {
  next: { revalidate: 60, tags: ["prices"] },
});

// elsewhere, after a mutation:
import { revalidateTag } from "next/cache";
revalidateTag("prices");

Opt in to caching explicitly; time-based and tag-based revalidation compose, and tags are the tool for on-demand invalidation.

Per-page metadatametadata-seo

// static
export const metadata = { title: "Pricing", description: "Plans" };

// dynamic
export async function generateMetadata({ params }) {
  const { slug } = await params;
  const post = await getPost(slug);
  return { title: post.title, openGraph: { images: [post.image] } };
}

Only Server Components can export metadata; client pages need a server wrapper.

Optimized imagesimage-optimization

import Image from "next/image";

<Image src="/hero.png" alt="Hero" width={1200} height={630} priority />

Remote images require allowing the host in images.remotePatterns in next.config; width/height (or fill) are mandatory to prevent layout shift.

Redirect and guard requests (proxy.ts)proxy-middleware

// proxy.ts (root, replaces middleware.ts in v16)
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function proxy(req: NextRequest) {
  if (!req.cookies.get("session") && req.nextUrl.pathname.startsWith("/app")) {
    return NextResponse.redirect(new URL("/login", req.url));
  }
  return NextResponse.next();
}

export const config = { matcher: ["/app/:path*"] };

v16 renamed middleware to proxy to signal it is for routing concerns, not auth logic; do real session checks in the page or layout too.

Shared layout with nested routeslayouts

// app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <section>
      <nav>...</nav>
      {children}
    </section>
  );
}

Layouts persist across navigation and do not re-render; state you want reset per page belongs in the page, not the layout.

Alternatives

PackageRegistryPick it when
react-routernpmSPA or app behind auth where you do not need SSR; also its framework mode covers SSR with less magic than Next
astronpmContent-heavy sites where most pages are static; ships near-zero JS by default and lets you drop in React islands
nuxtnpmYour team prefers Vue; it is the closest equivalent feature-for-feature