<R/>

Crafting digital experiences

<R/>Rofiq.Full-Stack Product Engineer
AboutServicesWorkBlogRésuméContact
AboutServicesWorkBlogRésuméContact
Let's talk

One project slot open

Got something worth building? Let's make it real.

Start a project
<R/>Rofiq RahmanFull-Stack Product Engineer

Full-stack product engineering for teams that want the thing shipped, measured and maintainable.

Explore

AboutServicesWorkBlogRésuméContact

More

RésuméPrivacy policyUses — tools, hardware and setupRSS feed

Connect

© 2026 Rofiq Rahman. Crafted with Next.js.

Back to top
All articles

Next.js

Server components changed how I structure a Next.js app

After six App Router projects, the folder structure I keep arriving at — and the three mistakes I made in the first two that cost me the most time.

Mr. Rofiq Rahman · Sep 24, 2026 · 11 min read

Code editor with a file tree, an open component file and a terminal below

On this page

  1. The rule that fixed everything
  2. The structure
  3. Three mistakes
  4. Where I still hesitate

I have now built six production apps on the App Router. The first two I structured like a Pages Router app with new syntax, and I paid for it. Here is where I ended up.

The rule that fixed everything

Server components fetch and compose. Client components handle interaction. The boundary is as deep in the tree as it can be.

Almost every problem I had in those first two projects came from putting "use client" too high. One interactive dropdown in a header turned the entire page into client-rendered JavaScript, and I did not notice for weeks because it still worked.

The structure

app/
  (site)/              server components, the public site
  admin/
    _actions/          server actions, grouped by domain
    _components/       admin-only UI
  api/                 only where a route handler is genuinely needed
lib/
  content/             read queries for the public site
  db/                  models and the connection
  validation/          zod schemas shared by forms and actions
components/ui/         design-system primitives

The thing worth copying is lib/validation. One schema validates the form in the browser and the payload on the server. When they drift, you get a type error instead of a bug.

Three mistakes

1. A context provider at the root. It makes everything below it a client component. If you need theme state, put the provider around the smallest subtree that uses it — or push the state into a cookie and read it on the server.

2. Fetching in a client component because it was easier. It was easier for twenty minutes. Then it was a loading spinner, a race condition and a waterfall. Fetch on the server, pass data down.

3. Treating server actions as a REST API. An action is a mutation plus a revalidation. If you find yourself returning rich data from one to drive the UI, you probably wanted a server component.

Where I still hesitate

Deeply interactive views — a drag-and-drop builder, a live chat panel — are client components all the way down, and that is correct. Trying to be clever with the boundary there buys nothing and costs clarity.

The split is not about minimising client JavaScript for its own sake. It is that data-fetching on the server removes a whole category of state you no longer have to manage.

  • #next.js
  • #react
  • #server components
  • #architecture
Share

Keep reading

Related articles

Lighthouse report with category scores, Core Web Vitals and a request waterfall

Performance · Oct 9, 2026 · 8 min read

A performance budget that survives the deadline

Every team agrees the site should be fast, right up until the marketing tag goes in on a Thursday. Here is the setup that makes the budget hold without anyone having to be the villain.

#performance#core web vitals#next.js
Storefront product listing with a promotional banner and product grid

UX Engineering · Sep 7, 2026 · 6 min read

Your loading spinner is probably making things worse

Skeletons and spinners feel like good UX. Measured against perceived performance, most of the ones I have shipped made the page feel slower. What to use instead.

#ux#performance#react
Budgeting dashboard showing envelope budgets and a transaction list

Backend · Aug 16, 2026 · 5 min read

Storing money: integers, always

A short post about the one bug I refuse to debug twice, and the three places floating-point currency still sneaks into an otherwise careful codebase.

#backend#databases#money