Zum Hauptinhalt springen
Technology Web Development

Hugo Mastery Part 3: Modern Features & Advanced Capabilities

Master Hugo's modern features including YAML anchors, HTML-to-Markdown conversion, efficient random sampling, and advanced configuration patterns.

Introduction

Modern Hugo has introduced several powerful features that address common development challenges: configuration duplication, inefficient content sampling, and complex content migrations. This guide covers the essential capabilities that separate basic Hugo sites from professionally architected ones.

You’ll learn about YAML anchors for DRY configuration, the collections.D function for efficient random sampling, transform.HTMLToMarkdown for content conversion, and module version control for better dependency management. Each feature solves specific production problems and scales with your project’s complexity.

Essential Modern Features

1. YAML Anchors for DRY Configuration

YAML anchors and aliases eliminate configuration duplication by allowing you to define settings once and reference them throughout your configuration. This is particularly valuable for multi-environment setups where most settings remain constant across development, staging, and production.

Basic Syntax

Before (repetitive):

production:
  author: John Doe
  language: en
  theme: modern
  baseURL: https://example.com

development:
  author: John Doe
  language: en
  theme: modern
  baseURL: http://localhost:1313

After (DRY with anchors):

defaults: &defaults
  author: John Doe
  language: en
  theme: modern

production:
  <<: *defaults
  baseURL: https://example.com

development:
  <<: *defaults
  baseURL: http://localhost:1313

Real-World Examples

Menu Configuration:

menu_defaults: &menu_defaults
 class: nav-item
 target: _self

menu:
 main:
 - <<: *menu_defaults
 name: Home
 url: /
 weight: 1

 - <<: *menu_defaults
 name: Blog
 url: /blog/
 weight: 2

 - <<: *menu_defaults
 name: About
 url: /about/
 weight: 3

Multilingual Setup:

lang_defaults: &lang_defaults
 disabled: false
 params:
 dateFormat: "2 January 2006"
 copyright: © 2025-2026 My Site

languages:
 en:
 <<: *lang_defaults
 languageName: English
 weight: 1

 de:
 <<: *lang_defaults
 languageName: Deutsch
 weight: 2
 params:
 dateFormat: "2. January 2006"

 fr:
 <<: *lang_defaults
 languageName: Français
 weight: 3

Environment-Specific Settings:

base_settings: &base
  buildDrafts: false
  buildFuture: false
  buildExpired: false

prod_settings: &prod
  <<: *base
  minify: true
  environment: production
  googleAnalytics: UA-XXXXX-Y

dev_settings: &dev
  <<: *base
  buildDrafts: true
  buildFuture: true
  environment: development

# Use in build configuration
build:
  <<: *prod # Switch to *dev for development

Breaking Change: Boolean Handling

Important Migration Required:

YAML 1.1 boolean strings are no longer auto-converted.

Old Behavior:

published: yes # Converted to true
draft: no # Converted to false
enabled: on # Converted to true
disabled: off # Converted to false

New Behavior:

published: true # Use explicit boolean
draft: false # Use explicit boolean
enabled: true # Use explicit boolean

# Or keep as strings
status: "yes" # String value "yes"

Migration Script:

# Search for old-style booleans
grep -r ": yes$\|: no$\|: on$\|: off$" config/
grep -r ": yes$\|: no$\|: on$\|: off$" content/

# Replace with proper booleans
# `: yes` → `: true`
# `: no` → `: false`
# `: on` → `: true`
# `: off` → `: false`

2. HTML to Markdown Conversion

The transform.HTMLToMarkdown function provides native HTML-to-Markdown conversion, essential for content migrations, generating LLM-friendly output, and creating plain text versions of rendered content. This eliminates the need for external conversion tools or custom regex solutions.

Basic Usage:

{{ $html := "<h1>Welcome</h1><p>This is <strong>bold</strong> text.</p>" }}
{{ $markdown := transform.HTMLToMarkdown $html }}
{{ $markdown }}
<! - Output: # Welcome\n\nThis is **bold** text. - >

Convert Page Content:

<! - Convert current page HTML to Markdown - >
{{ $markdown := transform.HTMLToMarkdown .Content }}

<! - Use for API endpoints or exports - >
{{ $markdown | safeHTML }}

Real-World Use Case: LLM-Friendly API:

<! - layouts/_default/llm.txt - >
{{- range .Site.RegularPages -}}
Title: {{ .Title }}
URL: {{ .Permalink }}
Date: {{ .Date.Format "2006-01-02" }}

Content:
{{ transform.HTMLToMarkdown .Content }}

---

{{ end }}

Use Case: Content Export:

<! - layouts/_default/export.md - >
{{- $pages := where .Site.RegularPages "Type" "posts" -}}
{{- range $pages -}}
# {{ .Title }}

{{ transform.HTMLToMarkdown .Content }}

---

{{ end }}

3. Efficient Random Sampling with collections.D

The collections.D function implements Vitter’s Method D for sequential random sampling, providing O(n) performance for selecting random items from large collections. This is significantly more efficient than shuffling entire collections when you only need a small sample.

For sites with hundreds or thousands of pages, this reduces build time and memory usage compared to traditional shuffle-and-slice approaches.

Syntax:

{{ collections.D $sampleSize $populationSize }}

Random Posts Sidebar:

<! - Select 5 random posts - >
{{ $allPosts := where .Site.RegularPages "Type" "posts" }}
{{ $indices := collections.D 5 (len $allPosts) }}

<aside class="random-posts">
 <h3>You Might Also Like</h3>
 {{ range $indices }}
 {{ $post := index $allPosts . }}
 <article>
 <h4><a href="{{ $post.Permalink }}">{{ $post.Title }}</a></h4>
 <p>{{ $post.Summary }}</p>
 </article>
 {{ end }}
</aside>

Random Image Gallery:

{{ $images := .Resources.Match "gallery/*" }}
{{ $totalImages := len $images }}
{{ $displayCount := 6 }}

{{ $randomIndices := collections.D $displayCount $totalImages }}

<div class="gallery">
 {{ range $randomIndices }}
 {{ $img := index $images . }}
 <img src="{{ $img.RelPermalink }}" alt="{{ $img.Name }}">
 {{ end }}
</div>

Random Testimonials:

{{ $testimonials := .Site.Data.testimonials }}
{{ $count := len $testimonials }}
{{ $randomIndices := collections.D 3 $count }}

<section class="testimonials">
 {{ range $randomIndices }}
 {{ $testimonial := index $testimonials . }}
 <blockquote>
 <p>{{ $testimonial.quote }}</p>
 <cite>{{ $testimonial.author }}</cite>
 </blockquote>
 {{ end }}
</section>

Performance Note: Method D is more efficient than shuffling the entire collection, especially with large datasets.

4. Module Version Control

Specify module versions directly in your Hugo configuration for better dependency management.

Before:

# Required manual go.mod editing or hugo mod get
hugo mod get github.com/user/theme@v1.2.3

After:

[[module.imports]]
 path = "github.com/user/theme"
 version = "v1.2.3"

Multi-Version Documentation:

# Maintain multiple API versions
[[module.imports]]
 path = "github.com/mycompany/api-docs"
 version = "v3.0.0"
 [[module.imports.mounts]]
 source = "content"
 target = "content/docs/v3"

[[module.imports]]
 path = "github.com/mycompany/api-docs"
 version = "v2.1.0"
 [[module.imports.mounts]]
 source = "content"
 target = "content/docs/v2"

[[module.imports]]
 path = "github.com/mycompany/api-docs"
 version = "v1.5.0"
 [[module.imports.mounts]]
 source = "content"
 target = "content/docs/v1"

Development vs Production:

# config/production/config.toml
[[module.imports]]
 path = "github.com/user/theme"
 version = "v2.1.0" # Stable version

# config/development/config.toml
[[module.imports]]
 path = "github.com/user/theme"
 version = "main" # Latest development

Customize your URLs with powerful new permalink tokens.

New Tokens:

  • :sectionslug - Section slug for current page
  • :sectionslugs - All section slugs in path

Multilingual Example:

[permalinks]
 posts = "/:year/:sectionslug/:slug/"

# Results:
# English: /2025/blog/my-post/
# German: /2025/nachrichten/mein-beitrag/

Nested Sections:

[permalinks]
 docs = "/:sectionslugs/:slug/"

# Results:
# /docs/getting-started/installation/
# /docs/advanced/optimization/

6. Enhanced Footnote Control

Gain precise control over how footnotes render in your content.

Configuration:

[markup.goldmark.extensions.footnote]
 returnLinkContents = "↩"
 autoPrefix = true # Auto-prefix footnote IDs to prevent conflicts

Custom Backlink HTML:

[markup.goldmark.extensions.footnote]
 backlinkHTML = '<sup><a href="#fnref:%s" class="footnote-backref" role="doc-backlink">↩︎</a></sup>'

Why This Matters:

Without autoPrefix, footnote IDs can conflict across pages in single-page applications or when combining content.

7. Visual Build Progress

Modern terminals display visual progress bars during builds.

Supported Terminals:

  • Ghostty
  • Windows Terminal
  • Other terminals supporting OSC 9;4 protocol

What You See:

Building sites … ████████████░░░░░░░░ 60%

No Configuration Required: Works automatically if your terminal supports it.

8. Modern Go Runtime

Hugo runs on the latest Go runtime, bringing:

  • Performance improvements
  • Better error messages
  • Enhanced security (10 security patches)
  • Improved standard library

Security & Performance

Built-in Security

Hugo incorporates the latest security patches and best practices:

Best Practices:

[security]
 enableInlineShortcodes = false

 [security.exec]
 allow = ['^dart-sass-embedded$', '^go$', '^npx$', '^postcss$']
 osEnv = ['(?i)^(PATH|PATHEXT|APPDATA|TMP|TEMP|TERM)$']

 [security.funcs]
 getenv = ['^HUGO_', '^CI$']

 [security.http]
 methods = ['(?i)GET|POST']
 urls = ['.*']

Continuous Improvements

Hugo continuously improves with enhanced:

  • CJK Support: Proper handling of Chinese/Japanese/Korean text in truncation
  • Shortcodes: Reliable nesting and error messages
  • Live Reload: Instant rebuilds when adding content bundles
  • Cross-Platform: Improved Windows and Unix compatibility
  • Type Handling: Robust YAML and data type processing

Migration Guide

YAML Boolean Migration

Step 1: Find Issues

grep -r ": yes$\|: no$\|: on$\|: off$" config/
grep -r ": yes$\|: no$\|: on$\|: off$" content/

Step 2: Replace

OldNew
: yes: true
: no: false
: on: true
: off: false

Step 3: Test

hugo - debug

Module Version Migration

Step 1: Check Current Versions

hugo mod graph

Step 2: Add to Config

[[module.imports]]
 path = "github.com/user/module"
 version = "v1.2.3" # Use version from hugo mod graph

Step 3: Clean and Update

hugo mod clean
hugo mod get -u

Practical Examples

Example 1: Complete Modern Configuration

# hugo.yaml
baseURL: https://example.com
languageCode: en-us
title: My Modern Site

# Use YAML anchors for DRY config
_defaults: &defaults
 author: John Doe
 theme: modern

_prod: &prod
 <<: *defaults
 minify: true
 environment: production

_dev: &dev
 <<: *defaults
 buildDrafts: true
 environment: development

# Current environment (switch based on HUGO_ENVIRONMENT)
params:
 <<: *prod

module:
 imports:
 - path: github.com/user/theme
 version: v2.1.0
 - path: github.com/user/components
 version: main

markup:
 goldmark:
 extensions:
 footnote:
 autoPrefix: true
 returnLinkContents: "↩"

Example 2: Random Content Showcase

<! - layouts/partials/random-showcase.html - >
{{ $allContent := where .Site.RegularPages "Type" "in" (slice "posts" "projects") }}
{{ $totalItems := len $allContent }}
{{ $showCount := 4 }}

{{ if gt $totalItems $showCount }}
 {{ $randomIndices := collections.D $showCount $totalItems }}

 <section class="random-showcase">
 <h2>Discover More</h2>
 <div class="showcase-grid">
 {{ range $randomIndices }}
 {{ $item := index $allContent . }}
 <article class="showcase-item">
 <h3><a href="{{ $item.Permalink }}">{{ $item.Title }}</a></h3>
 <p>{{ $item.Summary }}</p>
 <span class="type">{{ $item.Type }}</span>
 </article>
 {{ end }}
 </div>
 </section>
{{ end }}

Example 3: Content API with Markdown

<! - layouts/_default/api.json - >
{{- $pages := where .Site.RegularPages "Type" "posts" | first 50 -}}
{{- $items := slice -}}
{{- range $pages -}}
 {{- $item := dict
 "title" .Title
 "url" .Permalink
 "date" (.Date.Format "2006-01-02")
 "summary" .Summary
 "content_html" .Content
 "content_markdown" (transform.HTMLToMarkdown .Content)
 "tags" .Params.tags
 -}}
 {{- $items = $items | append $item -}}
{{- end -}}
{{- dict "items" $items | jsonify -}}

Best Practices

Alright, you’ve seen the features. Now let’s talk about how to actually use them without shooting yourself in the foot.

1. Use YAML Anchors (Seriously, Use Them)

I can’t stress this enough: if you’re still copying and pasting config blocks, you’re doing it wrong. YAML anchors will save you so much pain:

# Good
defaults: &defaults
 setting1: value1
 setting2: value2

config1:
 <<: *defaults
 specific: override

# Bad - repetitive
config1:
 setting1: value1
 setting2: value2
 specific: override

2. Pin Module Versions

Ensure reproducible builds:

# Good - explicit versions
[[module.imports]]
 path = "github.com/user/theme"
 version = "v2.1.0"

# Acceptable for development
[[module.imports]]
 path = "github.com/user/experimental"
 version = "main"

3. Enable Auto-Prefix for Footnotes

Prevent ID conflicts:

[markup.goldmark.extensions.footnote]
 autoPrefix = true

4. Use collections.D for Random Content

More efficient than shuffling:

<! - Good - >
{{ $random := collections.D 5 (len .Pages) }}

<! - Less efficient - >
{{ $shuffled := shuffle .Pages | first 5 }}

5. Keep Hugo Updated

# Check version
hugo version

# Update (macOS with Homebrew)
brew upgrade hugo

# Verify
hugo version

Conclusion

Modern Hugo provides sophisticated tools for professional static site development: YAML anchors reduce configuration overhead, collections.D enables efficient random sampling, transform.HTMLToMarkdown simplifies content migration, and direct module version control ensures reproducible builds.

Implementing these features improves code maintainability, build performance, and developer workflow. Start with YAML anchors for immediate configuration cleanup, then integrate collections.D where random sampling is needed. Use transform.HTMLToMarkdown for content migrations and API endpoints.

For ongoing updates, monitor Hugo releases and participate in the Hugo community.

Part 4 covers Hugo’s comprehensive template function library with production examples.

Resources


This is Part 3 of the Hugo Mastery series. ← Back to Part 2 | Continue to Part 4: Template Functions Mastery →

/Users/damirmukimov/projects/samyrai.github.io/layouts/partials/hardware-schema.html