How SSG Compiles JavaScript Components into HTML
Static Site Generation (SSG) is a build-time rendering process that transforms modern JavaScript framework components—such as React, Vue, or Svelte—into fully formed, static HTML, CSS, and client-side JavaScript files. Instead of waiting for a user to request a page and rendering it dynamically on a server or in the browser, an SSG build engine pre-renders every page ahead of time using a Node.js runtime. This article explains the technical pipeline that converts dynamic component trees into static markup, from route resolution and data fetching to server rendering and client hydration.
1. Build Initialization and Route Discovery
The compilation process begins when you run a build command (e.g.,
npm run build). The static site generator initializes a
Node.js build environment and scans the project structure to determine
all available routes.
For dynamic routes that depend on external data (such as
/blog/[slug]), the build tool executes specific discovery
functions (like Next.js’s generateStaticParams or Astro’s
getStaticPaths). These functions query APIs, databases, or
local Markdown/MDX files to generate a comprehensive list of URL paths
that must be pre-rendered.
2. Build-Time Data Fetching
Once the routes are identified, the generator executes any associated data-fetching logic for each page. Because this runs entirely in the build environment on the server or developer machine: * Secrets and API tokens remain protected and are never sent to the client. * Asynchronous data calls retrieve raw JSON data needed to populate component properties (props). * The resolved data is stored temporarily in memory and serialized into JSON files alongside the generated HTML.
3. Component Execution via Node.js
With the routes and page data ready, the build engine imports the JavaScript/TypeScript component files. Since components are typically written using dynamic syntax like JSX or Vue templates, the build tool (using bundlers like Webpack, Rollup, or Vite) transpiles this code into standard JavaScript that Node.js can execute.
The compiler then executes the root component for each route, passing the fetched data into the component tree as props. This step constructs the in-memory Virtual DOM representing the final state of the page.
4. Rendering to HTML Strings
To convert the Virtual DOM into real HTML, the engine invokes
server-rendering utilities provided by the underlying framework. For
example, in a React-based SSG, the system calls
renderToString() or renderToStaticMarkup()
from the react-dom/server package.
This utility traverses the component tree, evaluates conditions and
loops, strips out server-only logic, and serializes the elements into a
single, raw HTML string. This string is then injected into a base HTML
document shell, complete with proper <head>,
<meta>, and <title> tags.
5. Asset Compilation and File Emission
The generated HTML string is written to disk as a physical
.html file inside an output directory (such as
dist, out, or public).
Concurrently, the compiler: * Extracts and optimizes CSS styles, placing
them into dedicated stylesheets or inlining critical CSS into the
<style> tags of the HTML. * Compiles, minifies, and
splits the client-side JavaScript into bundles. * Injects
<script> and <link> tags into the
static HTML to reference these bundles and stylesheets.
6. Preparing for Client-Side Hydration
For interactive websites, the static HTML includes references to the compiled JavaScript bundles. When a user requests the page, the browser instantly displays the pre-rendered HTML without waiting for JavaScript to execute.
Once the page loads, the linked JavaScript downloads and executes a process called “hydration.” Hydration preserves the existing HTML structure while attaching event listeners (such as click handlers and state management hooks) back onto the DOM nodes, transforming the fast-loading static HTML file into a fully interactive Single-Page Application (SPA).