From architectural insights to working implementation – the complete journey from frustration to functional publishing platform

hugo and netlify

(source: https://blog.viktoradam.net/2019/03/28/netlify-hugo/)


The Final Implementation

After understanding the architectural foundations that make static site generators so powerful, it was time to build the actual system. This final episode documents the complete process – from installing Hugo to deploying on Netlify – with practical insights from actually building and launching my new technical blog.

The goal was to create a sustainable, maintainable publishing platform that would eliminate the copy-paste formatting nightmares I’d been experiencing. What I discovered was that the implementation was significantly more straightforward than I’d anticipated, taking roughly 4-6 hours total to go from zero to a fully functional technical publishing platform.

You can see the final result here at debugndiscover.netlify.app – a clean, fast-loading technical blog that handles mathematical equations, syntax highlighting, and complex diagrams without any of the manual formatting work that plagued my Medium workflow.


Prerequisites: Setting Your Foundation

Before diving into Hugo installation, ensure you have the essential tools for modern development workflows.

Git version control is essential for any serious publishing platform. If you don’t have Git installed, visit git-scm.com for installation instructions. Git transforms your content from isolated files into a complete project history, enabling version control and automated deployment workflows.

A GitHub account connects your local work to deployment systems. Sign up at github.com if needed. GitHub serves as both content backup and the trigger for automatic site rebuilds when you publish new posts.

A modern text editor improves your writing experience significantly. I use VS Code but any well-received editors you’re comfortable with (e.g., well-customized neovim) will suffice.

These tools create a professional content management workflow: write in your preferred editor, track changes with Git, collaborate through GitHub, and deploy automatically to the web.


Installing Hugo: Simpler Than Expected

Hugo installation varies by operating system, but the process is straightforward. I’ll cover the two platforms I use regularly – if you’re on a different system, Hugo’s official installation guide covers all platforms comprehensively.

macOS Installation

Homebrew provides the cleanest installation experience on macOS:

1
2
3
4
5
# Install Homebrew if you don't have it
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Install Hugo extended edition
brew install hugo

The extended edition includes advanced features like Sass processing and WebP image encoding that many themes require. This single command handles dependencies, adds Hugo to your PATH, and keeps you on the latest stable version.

Ubuntu Installation

For Ubuntu, snap packages ensure you get the latest version:

1
2
# Install Hugo extended via snap
sudo snap install hugo --channel=extended

The snap package automatically updates and includes the extended features needed for modern Hugo themes. If you prefer apt, it’s available but often lags behind the current release.

Verifying Installation

Test your installation:

1
2
3
4
5
# Check Hugo version and features
hugo version

# Should output something like:
# hugo v0.147.9+extended linux/amd64 BuildDate=2025-06-28T12:00:00Z

The +extended indicator confirms you have the full-featured version needed for advanced themes.


Creating Your Hugo Site

Generate your site skeleton:

1
2
3
# Create a new Hugo site
hugo new site my-technical-blog --format yaml
cd my-technical-blog

Hugo creates a logical directory structure that separates content from presentation:

1
2
3
4
5
6
my-technical-blog/
├── content/             # Your blog posts and pages
├── static/              # Images, PDFs, and files served directly
├── themes/              # Hugo themes
├── assets/              # Source files for processing
└── hugo.yaml           # Main configuration file

This separation enables you to change themes without losing content or switch content management approaches without breaking your design.


Installing PaperMod Theme

After evaluating multiple Hugo themes, I chose PaperMod for its excellent balance of features, performance, and maintainability.

Install PaperMod via Git submodule:

1
2
3
4
# Initialize Git and add PaperMod
git init
git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod
git submodule update --init --recursive

Git submodules connect your site to the theme’s repository while keeping your customizations separate. When the theme updates, you can update cleanly without losing modifications.

Configure PaperMod by creating your hugo.yaml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# Base site configuration
baseURL: "https://your-site-name.netlify.app"  # Your site's URL (update when deploying)
title: "Your Technical Blog"                   # Site title shown in browser tabs
theme: "PaperMod"                             # Theme name (must match folder in themes/)

# Markdown and syntax highlighting configuration
markup:
  goldmark:
    extensions:
      passthrough:
        enable: true                          # Enable LaTeX passthrough for math rendering
        delimiters:
          block: [['\\[', '\\]'], ['$$', '$$']]   # Block math delimiters
          inline: [['\\(', '\\)'], ['$', '$']]    # Inline math delimiters
    renderer:
      unsafe: true                            # Allow HTML in markdown (needed for custom shortcodes)
  highlight:
    lineNos: true                            # Show line numbers in code blocks
    lineNumbersInTable: true                 # Use table format for line numbers (better for copying)
    style: github                            # Syntax highlighting style
    codeFences: true                         # Enable ``` code fence syntax
    guessSyntax: true                        # Auto-detect language if not specified

# PaperMod theme configuration
params:
  defaultTheme: auto                         # Theme mode: light, dark, or auto (follows system)
  ShowReadingTime: true                      # Display estimated reading time
  ShowCodeCopyButtons: true                  # Add copy buttons to code blocks
  
  # Homepage configuration
  homeInfoParams:
    Title: "Welcome to Your Technical Blog! 👋"
    Content: "Sharing insights on technology, research, and development."

# Navigation menu configuration
menu:
  main:
    - name: Posts                            # Menu item name
      url: /posts/                           # URL path
      weight: 20                             # Lower numbers appear first
    - name: Tags
      url: /tags/
      weight: 40

This configuration enables syntax highlighting with line numbers, code copy buttons, and responsive light/dark themes.


Implementing Mathematical Notation and Mermaid Diagrams

While PaperMod provides built-in support for math and diagrams, I found their implementations unreliable during testing. Instead, I implemented custom solutions that provide guaranteed functionality and better control over rendering.

Understanding the Implementation Strategy

The approach uses Hugo’s template system to conditionally load JavaScript libraries only when needed. This keeps pages fast while ensuring mathematical equations and diagrams render perfectly when present.

Create the necessary layout directories:

1
2
3
4
# Create layout override directories
mkdir -p layouts/partials
mkdir -p layouts/shortcodes
mkdir -p layouts/_default

These directories allow you to extend PaperMod’s functionality without modifying the theme directly.

Mathematical Notation with MathJax

Create layouts/partials/mathjax_support.html:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
<script>
MathJax = {
  tex: {
    displayMath: [['\\[', '\\]'], ['$$', '$$']],    // Block math delimiters
    inlineMath: [['\\(', '\\)'], ['$', '$']],       // Inline math delimiters  
    processEscapes: true,                           // Process backslash escapes
    processEnvironments: true                       // Process LaTeX environments
  },
  options: {
    skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']  // Don't process math in these tags
  }
};
</script>

This configuration matches the delimiters we specified in hugo.yaml, ensuring consistent math rendering throughout your site.

Mermaid Diagram Implementation

Mermaid requires two separate implementations to handle both shortcode syntax and code fence syntax (````mermaid`). Note: While both implementations are provided, the code fence approach doesn’t work reliably in current testing. I recommend using the shortcode syntax for consistent results.

Create layouts/shortcodes/mermaid.html:

1
2
3
<div class="mermaid" align="{{ if .Get "align" }}{{ .Get "align" }}{{ else }}center{{ end }}">
{{ .Inner }}
</div>

This shortcode handles our mermaid shortcode syntax and allows optional alignment parameters.

Create layouts/_default/render-codeblock-mermaid.html:

1
2
3
4
<pre class="mermaid">
{{ .Inner | htmlEscape | safeHTML }}
</pre>
{{ .Page.Store.Set "hasMermaid" true }}

This template processes ````mermaid` code fences and sets a page variable to trigger JavaScript loading, though this method currently has reliability issues.

Create layouts/partials/mermaid.html:

1
2
3
4
5
6
7
8
<script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
<script>
  mermaid.initialize({
    startOnLoad: true,                      // Automatically render diagrams when page loads
    theme: 'default',                       // Mermaid theme (default, dark, forest, etc.)
    securityLevel: 'loose'                  // Allow more HTML features in diagrams
  });
</script>

Conditional Loading System

Create layouts/partials/extend_head.html:

1
2
3
4
5
6
{{ if .Params.math }}
  {{ partial "mathjax_support.html" . }}
{{ end }}
{{ if .Params.mermaid }}
  {{ partial "mermaid.html" . }}
{{ end }}

This partial checks your post’s frontmatter for math: true or mermaid: true and loads the appropriate libraries.

Create layouts/partials/extend_footer.html:

1
2
3
4
5
6
7
8
9
{{ if .Store.Get "hasMermaid" }}
<script type="module">
  import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs';
  mermaid.initialize({ 
    startOnLoad: true,
    theme: 'default'
  });
</script>
{{ end }}

This handles the modern ES module loading for code fence Mermaid diagrams, using Hugo’s page store to detect when Mermaid content is present.

Why This Approach Works Better

This implementation provides several advantages over theme defaults:

Conditional loading – JavaScript libraries only load on pages that need them, keeping other pages fast Reliable syntax support – The shortcode approach works consistently (code fence support is included but currently unreliable) Version control – You can pin specific library versions for consistent rendering Customization – Easy to modify themes, delimiters, or initialization options

The separation between extend_head.html and extend_footer.html ensures MathJax loads early (in the head) while Mermaid can load later (in the footer) without affecting page rendering performance.


Testing Your Setup

Start Hugo’s development server:

1
hugo server --buildDrafts

Navigate to http://localhost:1313 to see your blog. The development server includes live reload – changes automatically refresh in your browser.

Create a test post:

1
hugo new posts/test-post.md

Edit the generated file to test your features.


Deployment with Netlify

Netlify transforms your GitHub repository into a live website with global CDN distribution and automatic SSL certificates. The process is remarkably straightforward.

Prepare for deployment:

1
2
3
4
5
6
7
8
# Create .gitignore
echo "public/
.hugo_build.lock
resources/_gen/" > .gitignore

# Commit your site
git add .
git commit -m "Initial Hugo site with PaperMod"

Create a GitHub repository and push your code:

1
2
3
4
# Add GitHub remote (create repo on GitHub first)
git remote add origin https://github.com/yourusername/your-blog.git
git branch -M main
git push -u origin main

Deploy on Netlify:

  1. Sign up at netlify.com using your GitHub account
  2. Click “Add new site” → “Import an existing project”
  3. Choose “Deploy with GitHub” and select your repository
  4. Configure build settings:
    • Build command: hugo
    • Publish directory: public
    • Add environment variable: HUGO_VERSION = 0.147.9
  5. Deploy your site

Create netlify.toml in your site root for consistent builds:

1
2
3
4
5
6
[build]
publish = "public"              # Directory containing built site
command = "hugo"                # Build command to run

[build.environment]
HUGO_VERSION = "0.147.9"        # Pin Hugo version for consistent builds

Your site deploys automatically whenever you push to GitHub. Netlify assigns you a subdomain, but you can easily connect a custom domain later.


The Workflow in Practice

Your new publishing workflow eliminates the copy-paste formatting problems entirely:

  1. Write posts locally in markdown with full mathematical and code support
  2. Preview with live reload to see exactly how content will appear
  3. Commit and push to GitHub to trigger automatic deployment
  4. Site updates live within 30-60 seconds

The mathematical equations I struggled with on Medium now render beautifully. Code blocks maintain proper syntax highlighting and formatting. Complex diagrams display as intended. The semantic structure that was lost in copy-paste workflows is preserved completely.


Time Investment: Pleasantly Surprised

The entire implementation took significantly less time than I’d anticipated:

  • Hugo installation and basic setup: 1-2 hours – mostly spent understanding the directory structure and basic configuration
  • Netlify deployment setup: 1 hour – including account creation, repository connection, and first deployment
  • PaperMod customization: 2-3 hours – configuring mathematical notation, adjusting colors, and setting up the homepage

Total time: 4-6 hours from zero to a fully functional technical publishing platform.

Most of this time was spent understanding how the pieces fit together rather than fighting with complex configurations. The documentation for both Hugo and PaperMod is excellent, and the error messages are helpful when something goes wrong.

What surprised me most was how quickly the development workflow became natural. Within a day of using the system, writing and publishing felt more fluid than it ever had on traditional platforms.


The Real-World Result

The proof is in the execution. This blog demonstrates what this system is capable of:

  • Clean, fast-loading pages that prioritize content readability
  • Perfect mathematical notation rendering without image conversion workflows
  • Syntax highlighting that makes code examples genuinely helpful
  • Responsive design that works across devices
  • Sub-second page loads from global CDN distribution

Migrating Existing Content

The transition process proved surprisingly smooth when migrating my existing Medium articles. The migration primarily involved:

Adding proper frontmatter to each post. For example:

1
2
3
4
5
6
7
8
---
title: "Quest for Better Blogging: #1 Why I'm Evolving Beyond Medium"
author: "Sae-Hwan Park"
date: 2025-07-04
math: true
mermaid: true
draft: false
---

Converting Mermaid syntax from code fences to Hugo shortcodes:

  • Changed from ````mermaid` code blocks to the mermaid shortcodes for reliable rendering
  • A straightforward find-and-replace operation that took seconds per diagram

The remarkable part: Content that had required extensive manual formatting on Medium – mathematical equations, code blocks, complex diagrams – transferred seamlessly and now renders better than the original versions. Equations that were awkward image files on Medium became crisp, scalable mathematical notation. Code that lost syntax highlighting in copy-paste workflows now displays with proper formatting and copy buttons.

The contrast with my previous Medium workflow is stark. What used to require an hour of manual formatting corrections now takes zero additional time beyond writing the actual content.


Was It Worth the Investment?

The transition from Medium to this Hugo-based system represents more than just a technical upgrade – it’s a fundamental shift in how I approach technical communication.

The immediate benefits are clear: no more formatting battles, perfect equation rendering, and a publishing workflow that amplifies rather than constrains my thinking. Mathematical notation, code examples, and complex diagrams now enhance my explanations instead of creating obstacles.

The long-term advantages are even more significant: complete control over presentation, content that remains portable across platforms, and a system that can evolve with changing needs. I own every aspect of the publishing pipeline, from content creation to reader experience.

The 4-6 hour investment eliminated what had become hours of weekly formatting frustration. More importantly, it removed the mental overhead of wondering whether a technical concept could be properly expressed within platform limitations.


Final Reflection

The transition from Medium to this Hugo-based system addressed a specific workflow problem: the time overhead of formatting technical content. Medium never restricted my ideas, but it consistently consumed extra hours per post dealing with equation images, code formatting issues, and diagram placement, which the new Hugo setup eliminates entirely. The publishing workflow now matches the actual writing process – linear and predictable.

To my surprise, it took much fewer hours than anticipated without major hassles. More importantly, there’s a sense of ownership that subscription platforms can’t provide. The content, the presentation, and the entire publishing pipeline belong to me. If Hugo becomes unsuitable, the markdown files migrate cleanly to any other system. I also believe my migration experience this time will be transferable.

Given web originated from static sites, SSG may not be revolutionary, but as shown in my case, it can solve real problems for technical writers. When your content includes equations, code, and diagrams regularly, having tools that handle these elements naturally rather than as afterthoughts makes a measurable difference in productivity.