How Markdown to HTML and SSG Works

Building a website shouldn't require complex databases or server-side processing. SSG Portfolio demonstrates how simple, powerful tools can transform plain-text Markdown files into a complete static website. Let's explore how this magic happens.

What is Markdown?

Markdown is a lightweight, human-readable markup language created in 2004. Instead of writing complex HTML tags, you write simple, readable text:

# This is a heading
This is a paragraph with **bold** and *italic* text.

- Bullet point 1
- Bullet point 2

[Link text](https://example.com)

When processed, this becomes clean, semantic HTML. Markdown is perfect for content because it's:

  • Easy to write - Focus on content, not formatting
  • Version control friendly - Plain text works great with Git
  • Portable - Works anywhere, no proprietary formats
  • Readable - Even unprocessed, it's easy to read

The SSG Portfolio Workflow

Step 1: File Organization

All content lives in the /content/ directory as Markdown files (.md). Each file represents one post or page. The filename becomes the URL slug:

content/
├── my-article.md          → /my-article.html
├── getting-started.md     → /getting-started.html
└── images/                → /images/ (copied to dist)

Step 2: YAML Frontmatter

At the top of each Markdown file, we add metadata in YAML format:

---
title: "Article Title"
date: 2024-01-28
tags: ["tag1", "tag2"]
description: "Brief summary for search engines"
slug: "custom-url-slug"  # Optional
---

# Article content starts here...

Why frontmatter? It separates content metadata (title, date, tags) from the body. The generator uses this to:

  • Create page titles
  • Sort posts by date
  • Organize by tags
  • Generate meta tags for SEO

Step 3: Markdown Parsing

The build process uses three key Node.js packages:

1. gray-matter - Extracts frontmatter from Markdown files

const matter = require('gray-matter');
const file = matter.read('my-article.md');

console.log(file.data);   // { title, date, tags, ... }
console.log(file.content); // Raw markdown body

2. marked - Converts Markdown to HTML

const { marked } = require('marked');
const html = marked.parse('# Heading\n\nParagraph');

// Output: <h1>Heading</h1><p>Paragraph</p>

3. handlebars - Injects content into templates

const handlebars = require('handlebars');
const template = handlebars.compile(layoutHTML);
const finalHTML = template({ title, content, tags, date });

Step 4: Template System

SSG Portfolio uses Handlebars templates to define the visual structure. Templates have two types:

Layouts (/templates/layouts/) - Page structures:

  • post.hbs - Single post layout with header, content, footer
  • index.hbs - Homepage listing all posts
  • page.hbs - Generic page template

Partials (/templates/partials/) - Reusable components:

  • header.hbs - Site header
  • navigation.hbs - Navigation menu
  • footer.hbs - Site footer
  • post-card.hbs - Post card for listings
  • post-meta.hbs - Date and tags display

How Templates Work

A layout file looks like:

<!DOCTYPE html>
<html>
  <head>
    <title>{{title}} | {{site.title}}</title>
  </head>
  <body>
    {{> header}}
    {{> navigation}}
    
    <main>
      <h1>{{title}}</h1>
      {{{content}}}
    </main>
    
    {{> footer}}
  </body>
</html>

Template variables available:

  • {{title}} - Post title
  • {{content}} - Rendered HTML content
  • {{date}} - Publication date
  • {{tags}} - Array of tags
  • {{site.title}} - Site configuration
  • {{> partial-name}} - Include a partial

Notice {{{content}}} with three braces - that tells Handlebars NOT to escape HTML, so the Markdown-to-HTML conversion is preserved.

Step 5: The Build Process

When you run npm run build, the generator orchestrates everything:

┌─────────────────┐
│  1. Load Config │ (site.config.js)
└────────┬────────┘
         │
┌────────▼──────────────┐
│  2. Parse Markdown    │ (gray-matter + marked)
│  - Extract frontmatter│
│  - Convert to HTML    │
│  - Sort by date       │
└────────┬──────────────┘
         │
┌────────▼────────────────┐
│  3. Register Partials   │ (Load all .hbs files)
└────────┬────────────────┘
         │
┌────────▼──────────────────────────┐
│  4. Generate Individual Posts      │
│  - Use post.hbs layout            │
│  - Inject each post's content     │
│  - Write to /dist/*.html          │
└────────┬──────────────────────────┘
         │
┌────────▼──────────────────────────┐
│  5. Generate Index/Homepage        │
│  - Use index.hbs layout           │
│  - Loop through all posts         │
│  - Display as post cards          │
│  - Write index.html               │
└────────┬──────────────────────────┘
         │
┌────────▼──────────────────┐
│  6. Copy Static Assets    │
│  - Images (/content/images)
│  - CSS (/static/style.css)
│  - Other files            │
└────────┬──────────────────┘
         │
┌────────▼────────────────────┐
│  7. Output Complete          │
│  /dist/ ready for deployment │
└──────────────────────────────┘

Step 6: Output Structure

After the build, /dist/ contains:

dist/
├── index.html              # Homepage (lists all posts)
├── my-article.html         # Individual post pages
├── getting-started.html
├── style.css               # Stylesheet
├── images/                 # Copied from /content/images/
│   ├── example.jpg
│   └── subfolder/
│       └── nested.jpg
└── (any other static files)

Why This Architecture?

Advantages

Speed 🚀

  • No database queries
  • No server processing
  • Instant page loads
  • Perfect for CDN caching

Security 🔒

  • No server vulnerabilities
  • No SQL injection risks
  • No authentication needed
  • Just static files

Version Control 📝

  • All content in Git
  • Track changes with commits
  • Collaborate with branches
  • Full history preserved

Simplicity

  • No complex setup
  • No dependencies to manage
  • Easy to understand
  • Easy to customize

Portability 🌍

  • Deploy anywhere (Netlify, Cloudflare Pages, GitHub Pages, etc.)
  • No vendor lock-in
  • Works with any hosting
  • Can work offline

The Complete Flow: Example

Let's trace what happens to this file:

1. File: /content/my-article.md ```markdown

title: "My Article" date: 2024-01-28 tags: ["markdown", "tutorial"] description: "Learn about Markdown"

My Article

This is bold text.

  • Point 1
  • Point 2

**2. gray-matter extracts:**
```javascript
{
  data: {
    title: "My Article",
    date: "2024-01-28",
    tags: ["markdown", "tutorial"],
    description: "Learn about Markdown"
  },
  content: "# My Article\n\nThis is **bold** text..."
}

3. marked converts content to:

<h1>My Article</h1>
<p>This is <strong>bold</strong> text.</p>
<ul>
  <li>Point 1</li>
  <li>Point 2</li>
</ul>

4. Handlebars renders with post.hbs:

<!DOCTYPE html>
<html>
  <head>
    <title>My Article | SSG Portfolio</title>
  </head>
  <body>
    <!-- header and nav partials -->
    <main>
      <article>
        <h1>My Article</h1>
        <p>📅 2024-01-28</p>
        <div class="tags">
          <span>markdown</span>
          <span>tutorial</span>
        </div>
        <h1>My Article</h1>
        <p>This is <strong>bold</strong> text.</p>
        <ul>
          <li>Point 1</li>
          <li>Point 2</li>
        </ul>
      </article>
    </main>
    <!-- footer partial -->
  </body>
</html>

5. Output: /dist/my-article.html

Browser receives clean, semantic HTML - no JavaScript processing needed!

Key Takeaways

  1. Markdown is the content format - simple, readable, version-control friendly
  2. YAML Frontmatter stores metadata - title, date, tags, description
  3. gray-matter separates frontmatter from content
  4. marked converts Markdown to HTML
  5. Handlebars injects content into template layouts
  6. Static output means fast, secure, portable websites

Next Steps

Now that you understand the process, you can:

  • Write new posts by creating .md files in /content/
  • Customize templates in /templates/
  • Modify styling in /static/style.css
  • Deploy to any static hosting platform

The beauty of SSG is that once generated, the HTML is pure, fast, and lightweight. No frameworks, no runtime dependencies - just clean code and great content.


Want to learn more? Check out the README for writing post guides, or the DESIGN.md document for customizing templates and styles.