Skip to content

Where does "use client" go? As low as possible

"use client" doesn't mark a component, it marks a boundary. Everything imported below it ships to the browser. Here's how to keep it in check.

Author
Jan Jarčík
Published
Reading time
2 min read
Updated
ShareXLinkedInBluesky

In apps with Server Components (Next.js App Router) components are server components until you say otherwise. The "use client" directive doesn't say "this component is a client component" — it says "from here down, everything I import is part of the client bundle".

A common mistake

TSX
"use client"; // ❌ the whole page is now a client component
import { useState } from "react";
import { marked } from "marked"; // 40 kB shipped to the browser
 
export default function ProductPage({ product }) {
  const [qty, setQty] = useState(1);
  return (
    <article>
      <div dangerouslySetInnerHTML={{ __html: marked(product.description) }} />
      <input type="number" value={qty} onChange={(e) => setQty(+e.target.value)} />
    </article>
  );
}

Because of one input, an entire Markdown parser is sent to the browser.

Better: an interactive island

TSX
// ProductPage.tsx — Server Component, no directive
import { marked } from "marked"; // stays on the server
import { QuantityPicker } from "./QuantityPicker";
 
export default function ProductPage({ product }) {
  return (
    <article>
      <div dangerouslySetInnerHTML={{ __html: marked(product.description) }} />
      <QuantityPicker />
    </article>
  );
}
TSX
// QuantityPicker.tsx
"use client";
import { useState } from "react";
 
export function QuantityPicker() {
  const [qty, setQty] = useState(1);
  return <input type="number" value={qty} onChange={(e) => setQty(+e.target.value)} />;
}

Server children inside a client component

A client component can't import a server component, but it can receive one as children:

TSX
<Accordion>          {/* "use client" — holds the open state */}
  <ArticleBody />    {/* Server Component — rendered on the server */}
</Accordion>

ArticleBody renders on the server and the client only receives the finished result.

Rules of thumb

  • Put the directive on the leaves of the tree — buttons, inputs, animations.
  • Props crossing the boundary must be serializable (no functions except Server Functions).
  • Keep heavy libraries for formatting, Markdown or syntax highlighting on the server.