C.W.K.
Stream
Lesson 08 of 08 · published

Common Mistakes

~20 min · pitfalls, anti-patterns

Level 0Curious
0 XP0/68 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

Mistake 1: 'use client' at the top of the tree

Marking a layout or page as client turns everything below it into Client Components. You lose the server defaults for the entire subtree. Always promote the smallest leaf.

Mistake 2: Importing server-only modules from a Client file

Modules that touch the database, secrets, or Node-only APIs should be marked with import 'server-only'. If a Client Component imports them, the build fails — loud, fast, fixable.

Mistake 3: Building API routes for everything

App Router doesn't need an /api/… route to fetch data — Server Components do that directly. It doesn't need one for mutations either — Server Actions do that. Route Handlers exist for webhooks, third-party integrations, and genuinely public APIs. Internal CRUD belongs in Actions.

Mistake 4: Passing class instances as props

ORM models, custom Date subclasses, fancy Result types — they don't survive the serialization boundary. The plain fields cross; methods are gone. Convert to plain data before crossing.

Code

❌ Whole page demoted to client·tsx
'use client'; // Bad — all children become client too
import { useState } from 'react';

export default function Page() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <HeavyTable />     {/* Now ships JS for the whole table */}
      <MarkdownRenderer />{/* And the parser */}
      <button onClick={() => setOpen(!open)}>Toggle</button>
    </>
  );
}
✅ Only the toggle is client·tsx
// app/page.tsx — Server
export default async function Page() {
  const data = await loadAll();
  return (
    <>
      <HeavyTable data={data} />     {/* Server */}
      <MarkdownRenderer />            {/* Server */}
      <ToggleButton />                 {/* Client leaf */}
    </>
  );
}

// components/ToggleButton.tsx
'use client';
import { useState } from 'react';
export function ToggleButton() {
  const [open, setOpen] = useState(false);
  return <button onClick={() => setOpen(!open)}>{open ? 'Open' : 'Closed'}</button>;
}
✅ Fence server-only modules·ts
// lib/db.ts
import 'server-only';
import { PrismaClient } from '@prisma/client';
export const db = new PrismaClient();
// Importing this from a 'use client' file fails the build.

External links

Exercise

Audit your repo for files starting with 'use client'. For each one, ask: does the whole file need to be client, or only one leaf? Refactor at least two to push the directive down.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.