ToolSite

How Static Site Generators Turn Markdown Into HTML

How SSGs convert Markdown to HTML: parse frontmatter, render Markdown, inject templates. Code blocks, tables, MDX. Full pipeline. Try our free online converter.

By ToolSite5 min readguides

The Core Pipeline

Every static site generator (Next.js, Hugo, Astro, Jekyll, Eleventy) follows the same fundamental pipeline:

  1. Read a Markdown file from disk.
  2. Extract frontmatter (metadata at the top of the file).
  3. Convert the Markdown body to HTML.
  4. Inject the HTML into a template alongside the metadata.
  5. Write the final HTML file to the output directory.

This article walks through each step so you understand exactly what happens between writing ## Hello in a text editor and seeing <h2>Hello</h2> in a browser.

Step 1: Parse Frontmatter

A Markdown file for a static site starts with YAML frontmatter enclosed by --- delimiters:

---
title: "My First Post"
date: "2026-01-15"
tags: ["javascript", "tutorial"]
author: "Jane Developer"
---

## Getting Started

This is the body content. It contains **Markdown** formatting.

The generator splits the file at the --- markers. Everything between the first two --- lines is frontmatter, parsed as YAML (or TOML or JSON, depending on the generator). The rest is the Markdown body.

Frontmatter and body serve different purposes. Frontmatter provides structured data (title, date, tags, author) that templates can query. You can write {{ page.title }} in your template and it resolves to "My First Post". The body provides the prose content that renders into <main>.

Step 2: Convert Markdown to HTML

The Markdown body passes through a parser that produces an HTML string. The transformation is straightforward for basic syntax:

## Getting Started

This is **bold** and this is *italic*.

Here is a `code` span.

- Item one
- Item two
- Item three

Becomes:

<h2>Getting Started</h2>
<p>This is <strong>bold</strong> and this is <em>italic</em>.</p>
<p>Here is a <code>code</code> span.</p>
<ul>
  <li>Item one</li>
  <li>Item two</li>
  <li>Item three</li>
</ul>

The parser handles headings, emphasis, links, images, code blocks with language annotations, tables, blockquotes, and all standard Markdown syntax. Most generators use libraries like marked, remark, or markdown-it for this step.

Code Blocks and Syntax Highlighting

A fenced code block with a language tag:

```javascript
function greet(name) {
  return `Hello, ${name}`;
}
```

The Markdown parser alone produces:

<pre><code class="language-javascript">function greet(name) {
  return `Hello, ${name}`;
}</code></pre>

Notice there is no syntax highlighting yet. The language-javascript class is present but no token-level <span> elements exist. If you want highlighted code, a syntax highlighting plugin runs after the Markdown parser. It reads the language tag, tokenizes the code, and wraps each token (keyword, string, comment) in a <span> with a CSS class. The CSS theme provides the colors.

This highlighting step can happen at build time (generating static HTML) or at request time. Build time is better for performance because the browser receives already-highlighted code with zero JavaScript required.

Tables

Markdown tables convert to standard HTML <table> elements:

| Name | Price | Stock |
|------|-------|-------|
| Widget | $9.99 | 42 |
| Gadget | $14.99 | 7 |

Produces:

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Price</th>
      <th>Stock</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Widget</td>
      <td>$9.99</td>
      <td>42</td>
    </tr>
    <tr>
      <td>Gadget</td>
      <td>$14.99</td>
      <td>7</td>
    </tr>
  </tbody>
</table>

Step 3: Inject Into a Template

The raw HTML from Step 2 is not a complete page. It lacks <html>, <head>, navigation, and footer. The generator injects the rendered HTML and frontmatter into a template:

<!-- Template: layouts/post.html -->
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>{{ title }} - My Blog</title>
    <meta name="description" content="{{ description }}">
    <link rel="stylesheet" href="/styles.css">
  </head>
  <body>
    <nav>
      <a href="/">Home</a>
      <a href="/blog">Blog</a>
    </nav>
    <main>
      <article>
        <h1>{{ title }}</h1>
        <time datetime="{{ date }}">{{ date }}</time>
        {{ content }}
      </article>
    </main>
    <footer>
      <p>Built with a static site generator.</p>
    </footer>
  </body>
</html>

{{ title }} and {{ date }} are replaced with frontmatter values. {{ content }} is replaced with the rendered HTML body. The result is a complete, self- contained HTML page with navigation, styling, and metadata.

Step 4: Write to Disk

The final HTML is written to the output directory (typically dist/, _site/, or public/). The file path usually mirrors the source path:

src/posts/my-first-post.md   ->   public/posts/my-first-post/index.html

In development, files are served from memory and regenerated on every change. For production, the entire output directory is deployed to a static host or CDN. No server-side processing at request time. No database. Just flat HTML files.

What About MDX?

MDX extends Markdown with JSX. You can embed React components directly in Markdown files:

## Sales Dashboard

<BarChart data={salesData} />

Last quarter revenue was up 12 percent.

The MDX pipeline compiles this to JavaScript that renders React components. The output is not raw HTML. It is a React component that produces HTML when rendered. MDX powers component-driven documentation sites (like this blog) and lets you mix prose with interactive elements.

The pipeline is similar: parse frontmatter, compile MDX to a React component, wrap it in a layout component, and render to static HTML at build time. The extra step is the JSX compilation before the final HTML render.

Try it yourself: open the Markdown to HTML Converter. Type ## Hello **world** and watch the live HTML preview update. Check the raw HTML output to see the <h2> and <strong> tags. Then try a table, a fenced code block with a language tag, and a link to see exactly how each Markdown feature maps to HTML.

Related Reading