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

Why Server Components?

~20 min · bundle, perf, security

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

The bundle problem they solve

Traditional React ships every component's code to the browser as JavaScript. Markdown parsers, syntax highlighters, date utilities — all sent to every user, even though they only transform data once.

The four concrete wins

  1. Smaller bundles. Server Components contribute zero bytes to the client bundle. Heavy data-shaping libs stay on the server.
  2. Direct data access. Query Postgres, read files, call internal services without an API hop — same data center, same process, microsecond latency.
  3. Security. API keys, DB credentials, internal URLs never leave the server.
  4. Streaming. Server Components can stream HTML progressively. Cheap parts ship instantly, slow parts stream as data arrives.

The real-world bundle math

A blog post page using marked (~200KB) and highlight.js (~800KB) on the client costs every visitor 1MB of JavaScript. The same page as a Server Component ships zero bytes for those libraries; the rendered HTML is what reaches the browser.

Code

Heavy libraries stay on the server·tsx
import { marked } from 'marked';
import hljs from 'highlight.js';
import { format } from 'date-fns';
import { db } from '@/lib/db';

export default async function BlogPost({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const post = await db.post.findUnique({ where: { slug } });
  if (!post) return null;

  const html = marked(post.content, {
    highlight: (code, lang) => hljs.highlight(code, { language: lang }).value,
  });

  return (
    <article>
      <time>{format(post.date, 'MMMM d, yyyy')}</time>
      <div dangerouslySetInnerHTML={{ __html: html }} />
    </article>
  );
}
// Client receives: rendered HTML. Bundle impact: 0 bytes.
Read secrets safely·tsx
// app/admin/page.tsx — only reachable behind auth, but still a great place to see secret access
export default async function Admin() {
  const stripe = process.env.STRIPE_SECRET_KEY!;
  const { data } = await fetch('https://api.stripe.com/v1/customers', {
    headers: { Authorization: `Bearer ${stripe}` },
    cache: 'no-store',
  }).then(r => r.json());
  return <CustomersTable rows={data} />;
}

External links

Exercise

Find one heavy dependency in your package.json that's used purely for data shaping (markdown, syntax highlight, date util). Move its usage into a Server Component and confirm bundle analyzer shows it dropping out of the client bundle.

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.