Next.jsArchitectureStartupsTypeScript

The Next.js Architecture Mistake Every Startup Makes

Most startups reach for the wrong abstraction at the wrong time. Here's the pattern I see on almost every engagement — and how to avoid it.

L

Lazar Kapsarov

March 28, 2025 · 8 min read

Every startup I've worked with as a frontend engineer has made a version of the same mistake. It's not a bug. It's not a bad developer. It's a structural decision made too early — and it compounds silently until deployments take hours and every new feature feels like defusing a bomb.

The Mistake: Client-Side Everything

It usually starts innocently. A junior dev (or a senior dev in a hurry) reaches for useEffect + fetch to load data. It works. It ships. Then it gets copy-pasted twelve more times across the codebase.

Six months later:

  • Page loads show spinners for 3 seconds on every route
  • SEO is non-existent because content is rendered client-side
  • The API gets hammered because every component fetches its own data independently

What Should Have Happened

Next.js App Router gives you Server Components by default. That's not an accident — it's the architecture telling you something.

// ❌ What most teams do
"use client";
export function Dashboard() {
  const [data, setData] = useState(null);
  useEffect(() => {
    fetch("/api/metrics")
      .then((r) => r.json())
      .then(setData);
  }, []);
  return <MetricsChart data={data} />;
}

// ✅ What they should do
// app/dashboard/page.tsx — Server Component by default
export default async function Dashboard() {
  const data = await db.query.metrics.findMany();
  return <MetricsChart data={data} />;
}

The second version: no loading state, no client bundle weight, no waterfall, full SEO, zero API round-trip.

The Real Cost

The client-side-everything pattern doesn't just slow down pages. It creates a dependency on useEffect chains that are nearly impossible to test, reason about, or optimize later.

By the time a startup hires someone like me to fix it, the refactor takes weeks — not hours. Every component has to be audited. Every data fetch has to be re-evaluated.

How to Avoid It

Three rules I give every team at project kickoff:

  1. Default to Server Components. Only add 'use client' when you need interactivity (event handlers, browser APIs, local state).
  2. Fetch data as close to where it's used as possible — but on the server.
  3. Never fetch in a useEffect unless the data depends on user interaction after page load.

These three rules alone would have saved every startup I've audited from their worst performance problems.

The Broader Pattern

This isn't really about Next.js. It's about reaching for familiar patterns instead of understanding the constraints of the tool you're using.

React Server Components are a paradigm shift. They require unlearning the SPA mental model. Most teams don't take the time to do that — they just keep building SPAs inside a framework designed for something different.

The teams that do take the time? Their apps are faster, cheaper to run, and genuinely easier to maintain.

That's the architecture that outlives your roadmap.

:: NEXT STEP

Have a frontend architecture problem?

Book a free 30-minute strategy call. I'll audit your current setup and show you exactly where you're losing time and money.

Book Free Strategy Call →