Next.js

Building Fast Frontends with Next.js

Next.js has matured into the default choice for production React apps, but the framework gives you a lot of rope. Knowing which rendering mode to reach for - and when to stop reaching - is what separates a snappy site from a bundle-bloated one.

Laptop and desk

Picking the Right Rendering Mode

Every route is a decision. The App Router defaults to server components and static rendering, which is usually what you want - but the moment you reach for cookies(), headers(), or a dynamic segment without generateStaticParams, you've opted into SSR whether you meant to or not.

  • Static (SSG) for marketing pages, docs, and anything derived from content. Cheap, fast, cacheable at the edge.
  • Dynamic (SSR) for authenticated dashboards and personalized views. Pair with unstable_cache or revalidateTag so you aren't round-tripping the database on every request.
  • Client components only when you need state, effects, or browser APIs. Keep them at the leaves of the tree.

Keeping the Bundle Honest

The easiest performance win is shipping less JavaScript. A few habits that pay for themselves:

  1. Run @next/bundle-analyzer in CI and fail the build on regressions over a threshold.
  2. Prefer next/dynamic with ssr: false for heavy client-only widgets (charts, editors, video players).
  3. Audit your "use client" boundaries - a single misplaced directive can drag half your tree to the client.

Data Fetching That Scales

Colocate fetches with the components that need them, cache aggressively with tags, and invalidate with revalidateTag rather than blanket revalidatePath calls. For mutations, Server Actions keep request plumbing out of your components while TypeScript still catches shape mismatches end-to-end.

The boring parts - measuring, caching, and saying no to a fifth heading font - are what make a Next.js app feel fast a year after launch.

Next.jsPerformanceArchitecture

Lovro Hudrap

Next.js

Writing about next.js and the craft of building for the web.