Recraftory

SSR dan SSG

Server-Side Rendering dan Static Site Generation menggunakan Node.js tanpa framework

SSR (Server-Side Rendering) dan SSG (Static Site Generation) adalah dua pendekatan untuk menghasilkan HTML yang berbeda. Keduanya bertujuan meningkatkan performa dan SEO dibandingkan CSR (Client-Side Rendering) murni.

Perbedaan Fundamental

AspekSSRSSGCSR
Waktu renderSetiap requestBuild timeBrowser
HTML awalLengkap dari serverLengkap dari file/CDNKosong, diisi JS
SEOBaikTerbaikBuruk tanpa prerender
Dynamic dataReal-timePerlu rebuildReal-time
Server loadTinggiRendahRendah

SSR dengan Node.js Murni

SSR menghasilkan HTML di server untuk setiap request.

const http = require("http");

function renderPage(title, content) {
  return `<!DOCTYPE html>
<html>
<head><title>${title}</title></head>
<body>
  <h1>${title}</h1>
  <main>${content}</main>
  <script src="/app.js"></script>
</body>
</html>`;
}

const server = http.createServer((req, res) => {
  if (req.url === "/") {
    // Data bisa dari database, API, atau mana saja
    const posts = [
      { id: 1, title: "Hello" },
      { id: 2, title: "World" },
    ];

    const content = posts
      .map((p) => `<article><h2>${p.title}</h2></article>`)
      .join("");

    res.writeHead(200, { "Content-Type": "text/html" });
    res.end(renderPage("Blog", content));
  }
});

server.listen(3000);

Hydration

HTML yang di-render server perlu "dihidupkan" kembali di browser agar interaktif. Proses ini disebut hydration.

<!-- HTML dari server -->
<div id="root"><button>Click me</button></div>

<!-- JS di browser meng-attach event listener ke DOM yang sudah ada -->
<script>
  document.querySelector("button").addEventListener("click", () => {
    alert("clicked!");
  });
</script>

Di framework modern seperti React, hydration dilakukan oleh ReactDOM.hydrateRoot().

SSG dengan Node.js Murni

SSG menghasilkan file HTML saat build time.

const fs = require("fs");
const path = require("path");

function generatePage(route, content) {
  const html = `<!DOCTYPE html>
<html>
<head><title>${route}</title></head>
<body>${content}</body>
</html>`;

  const dir = path.join(__dirname, "dist", route);
  fs.mkdirSync(dir, { recursive: true });
  fs.writeFileSync(path.join(dir, "index.html"), html);
}

// Build time: generate semua halaman
const pages = [
  { route: "", content: "<h1>Home</h1>" },
  { route: "about", content: "<h1>About</h1>" },
  { route: "blog/hello", content: "<h1>Hello World</h1>" },
];

pages.forEach((p) => generatePage(p.route, p.content));

Hasil: folder dist/ dengan struktur seperti static file yang bisa di-serve oleh web server biasa.

Incremental Static Regeneration (ISR)

Kombinasi SSR dan SSG: halaman di-generate di build time, tapi dapat di-revalidate secara periodis atau on-demand tanpa full rebuild.

const cache = new Map();
const REVALIDATE_SECONDS = 60;

function getCachedPage(route, generator) {
  const cached = cache.get(route);
  const now = Date.now();

  if (cached && now - cached.time < REVALIDATE_SECONDS * 1000) {
    return cached.html;
  }

  const html = generator();
  cache.set(route, { html, time: now });
  return html;
}

Kapan Menggunakan Apa?

  • SSG — Blog, landing page, dokumentasi. Konten jarang berubah.
  • SSR — E-commerce, dashboard, sosial media. Konten personal atau sering berubah.
  • CSR — SPA internal, tool admin, aplikasi yang jarang butuh SEO.
  • ISR — Kombinasi yang ideal: performa SSG + freshness SSR.

Praktik Terbaik

  • Gunakan CDN untuk SSG output agar edge caching maksimal
  • Streaming SSR untuk Time to First Byte (TTFB) lebih cepat
  • Prioritaskan critical CSS di <head> HTML yang di-render server
  • Lazy load non-critical JS untuk mengurangi blocking