Goldmark Markdown Processing in Hugo
Goldmark Markdown Processing in Hugo
📖 Overview
Since Hugo 0.60, Goldmark is the default markdown parser, replacing BlackFriday. Goldmark is a CommonMark-compliant parser written in Go that offers better performance, spec compliance, and extensibility than its predecessor.
Why Goldmark Matters:
- CommonMark compliance - Consistent rendering across platforms
- Extensible - Support for tables, task lists, strikethrough, etc.
- Performant - Native Go implementation
- Customizable - Extensive configuration options
- Render hooks - Custom rendering for links, images, headings, code blocks
⚙️ Goldmark Configuration
Basic Configuration
config.toml:
[markup]
defaultMarkdownHandler = "goldmark"
[markup.goldmark]
[markup.goldmark.renderer]
unsafe = true # Allow raw HTML in markdown
hardWraps = false # Don't convert line breaks to <br>
xhtml = false # Don't use XHTML-style tags
[markup.goldmark.parser]
autoHeadingID = true # Generate heading IDs automatically
autoHeadingIDType = "github" # Use GitHub-style IDs
[markup.goldmark.parser.attribute]
block = true # Allow attributes on blocks
title = true # Allow attributes on titles
[markup.goldmark.extensions]
definitionList = true # Enable definition lists
footnote = true # Enable footnotes
linkify = true # Auto-link URLs
linkifyProtocol = "https" # Default protocol for linkify
strikethrough = true # Enable ~~strikethrough~~
table = true # Enable tables
taskList = true # Enable task lists
typographer = true # Smart quotes, dashesRenderer Options
unsafe = true vs false:
<!-- With unsafe = true (allows raw HTML) -->
<div class="alert alert-warning">
⚠️ This works!
</div>
<script>alert("This also works, be careful!")</script>
<!-- With unsafe = false (HTML escaped) -->
<div class="alert">This is shown as text, not rendered</div>When to use unsafe = true:
- ✅ You control all content
- ✅ Need custom HTML components
- ✅ Trusted content sources only
When to use unsafe = false:
- ✅ User-generated content
- ✅ Maximum security
- ✅ Enforce markdown-only content
Parser Options
autoHeadingID and autoHeadingIDType:
## My Heading Title
<!-- autoHeadingIDType = "github" -->
<h2 id="my-heading-title">My Heading Title</h2>
<!-- autoHeadingIDType = "github-ascii" -->
<h2 id="my-heading-title">My Heading Title</h2>
<!-- autoHeadingIDType = "blackfriday" (legacy) -->
<h2 id="my-heading-title">My Heading Title</h2>Attribute support:
{.myclass #myid key="value"}
## Heading with Attributes
<!-- Renders as: -->
<h2 class="myclass" id="myid" key="value">Heading with Attributes</h2>
{.warning}
This is a warning paragraph.
<!-- Renders as: -->
<p class="warning">This is a warning paragraph.</p>📝 Markdown Extensions
Tables
| Feature | Hugo/Goldmark | Quarto |
|---------|---------------|--------|
| Build Speed | ✅ Fast | 🟡 Moderate |
| Code Execution | ❌ No | ✅ Yes |
| Templates | Go templates | Pandoc |
<!-- Alignment -->
| Left | Center | Right |
|:-----|:------:|------:|
| Text | Text | Text |Task Lists
- [x] Install Hugo
- [x] Create new site
- [ ] Configure theme
- [ ] Write first post
- [x] Draft content
- [ ] Add images
- [ ] PublishRenders as:
- ☑ Install Hugo
- ☑ Create new site
-
- ☑ Draft content
Strikethrough
~~This text is crossed out~~
Price: ~~$99~~ $79 (20% off!)Renders as: This text is crossed out
Definition Lists
Hugo
: A fast static site generator
Goldmark
: CommonMark-compliant markdown parser written in Go
Quarto
: Scientific publishing system built on PandocRenders as:
- Hugo
- A fast static site generator
- Goldmark
- CommonMark-compliant markdown parser written in Go
- Quarto
- Scientific publishing system built on Pandoc
Footnotes
Hugo uses Goldmark[^1] for markdown processing, which replaced BlackFriday[^2].
[^1]: Goldmark is a CommonMark 0.30 compliant markdown parser.
[^2]: BlackFriday was the previous default parser before Hugo 0.60.Renders with footnote links and auto-generated footnote section.
Typographer
<!-- Smart quotes -->
"This is a quote"
'Single quote'
<!-- Em dashes -->
This is an em-dash: ---
This is an en-dash: --
<!-- Ellipsis -->
Waiting...
<!-- Renders as -->
"This is a quote" <!-- Curly quotes -->
'Single quote' <!-- Curly single quotes -->
This is an em-dash: —
This is an en-dash: –
Waiting…Auto-linking
<!-- linkify = true -->
Check out https://gohugo.io for more info.
Email me at info@example.com
<!-- Renders as -->
Check out <a href="https://gohugo.io">https://gohugo.io</a> for more info.
Email me at <a href="mailto:info@example.com">info@example.com</a>🎨 Syntax Highlighting with Chroma
Hugo includes Chroma, a syntax highlighter supporting 100+ languages.
Configuration
config.toml:
[markup.highlight]
anchorLineNos = false # Add anchors to line numbers
codeFences = true # Enable ``` code blocks
guessSyntax = false # Don't guess language if not specified
hl_Lines = "" # Highlight specific lines
hl_inline = false # Highlight inline code
lineAnchors = "" # Prefix for line anchors
lineNoStart = 1 # Starting line number
lineNos = false # Show line numbers
lineNumbersInTable = true # Use table for line numbers
noClasses = true # Use inline styles (vs CSS classes)
noHl = false # Disable highlighting
style = "monokai" # Color scheme
tabWidth = 4 # Tab width in spacesAvailable Styles
# List all available styles
hugo gen chromastyles --help
# Generate CSS for a specific style
hugo gen chromastyles --style=monokai > syntax.cssPopular styles:
monokai- Dark theme, popular for codegithub- GitHub-style light themedracula- Dark purple themevim- Classic vim colorsvs- Visual Studio light themesolarized-dark/solarized-light- Solarized themes
Code Blocks
Fenced code blocks:
```go
package main
import "fmt"
func main() {
fmt.Println("Hello, Hugo!")
}
```With language and options:
```go {linenos=table,hl_lines=[2,"4-6"],linenostart=1}
package main
import "fmt"
func main() {
fmt.Println("Hello, Hugo!")
}
```Options:
linenos=table- Line numbers in table (for copy-paste)linenos=inline- Line numbers inlinehl_lines=[2,5]- Highlight lines 2 and 5hl_lines=["4-6"]- Highlight lines 4 through 6linenostart=10- Start line numbers at 10anchorlinenos=true- Make line numbers linkable
Highlight Shortcode
Alternative to fenced code blocks with more control:
{{< highlight go "linenos=table,hl_lines=8 15-17,linenostart=1" >}}
package main
import "fmt"
func main() {
fmt.Println("Hello, Hugo!")
}
{{< /highlight >}}Inline Code Highlighting
config.toml:
[markup.highlight]
hl_inline = trueUsage:
Use the `var x = 10;`{language=javascript} syntax in your code.Practical Example: Learning Hub Code Display
Recommended configuration for technical documentation:
[markup.highlight]
anchorLineNos = true # Allow linking to specific lines
codeFences = true
guessSyntax = false # Require explicit language
lineNos = false # Don't show by default
lineNumbersInTable = true # Better for copy-paste
noClasses = false # Use CSS classes (better performance)
style = "github" # Professional light theme
tabWidth = 2 # Consistent with VS Code defaultsGenerate CSS:
hugo gen chromastyles --style=github > assets/css/syntax.cssInclude in template:
{{ $syntax := resources.Get "css/syntax.css" | minify }}
<link rel="stylesheet" href="{{ $syntax.Permalink }}">🎣 Render Hooks
Render hooks allow you to customize how specific markdown elements are rendered without modifying content files.
Link Render Hook
**layouts/_default/_markup/render-link.html:**
<a href="{{ .Destination | safeURL }}"
{{ with .Title }} title="{{ . }}"{{ end }}
{{ if strings.HasPrefix .Destination "http" }}
target="_blank"
rel="noopener noreferrer"
class="external-link"
{{ end }}>
{{ .Text }}
{{ if strings.HasPrefix .Destination "http" }}
<svg class="icon-external" aria-hidden="true">
<use xlink:href="#icon-external"></use>
</svg>
{{ end }}
</a>Features:
- ✅ External links open in new tab
- ✅ Security attributes (
rel="noopener") - ✅ Visual indicator for external links
- ✅ Preserves title attributes
Image Render Hook
**layouts/_default/_markup/render-image.html:**
{{ $image := .Page.Resources.GetMatch .Destination }}
{{ if $image }}
{{/* Image is a page resource */}}
{{ $resized := $image.Resize "800x" }}
{{ $webp := $resized.Process "webp" }}
<figure>
<picture>
<source srcset="{{ $webp.RelPermalink }}" type="image/webp">
<img src="{{ $resized.RelPermalink }}"
alt="{{ .Text }}"
width="{{ $resized.Width }}"
height="{{ $resized.Height }}"
loading="lazy"
{{ with .Title }}title="{{ . }}"{{ end }}>
</picture>
{{ with .Title }}
<figcaption>{{ . }}</figcaption>
{{ end }}
</figure>
{{ else }}
{{/* External image or static file */}}
<img src="{{ .Destination | safeURL }}"
alt="{{ .Text }}"
loading="lazy"
{{ with .Title }}title="{{ . }}"{{ end }}>
{{ end }}Features:
- ✅ Auto-resize images to 800px width
- ✅ Generate WebP versions
- ✅ Lazy loading
- ✅ Responsive images
- ✅ Figure/figcaption for titles
Heading Render Hook
**layouts/_default/_markup/render-heading.html:**
<h{{ .Level }} id="{{ .Anchor | safeURL }}">
{{ .Text }}
<a href="#{{ .Anchor | safeURL }}"
class="heading-anchor"
aria-label="Link to {{ .Text }}">
<svg class="icon-link" aria-hidden="true">
<use xlink:href="#icon-link"></use>
</svg>
</a>
</h{{ .Level }}>Features:
- ✅ Linkable headings (like GitHub)
- ✅ Accessible labels
- ✅ Custom styling
Code Block Render Hook
**layouts/_default/_markup/render-codeblock-bash.html:**
{{ $content := .Inner }}
<div class="code-block bash-block">
<div class="code-header">
<span class="language">Bash</span>
<button class="copy-button"
data-clipboard-text="{{ $content }}">
Copy
</button>
</div>
<pre><code class="language-bash">{{ $content }}</code></pre>
</div>Language-specific hooks:
render-codeblock-bash.htmlrender-codeblock-powershell.htmlrender-codeblock-python.htmlrender-codeblock-go.html
Features:
- ✅ Language-specific styling
- ✅ Copy button
- ✅ Custom headers
- ✅ Enhanced UX
🆚 Goldmark vs Quarto/Pandoc Markdown
| Feature | Goldmark (Hugo) | Pandoc (Quarto) |
|---|---|---|
| Spec Compliance | CommonMark 0.30 | Pandoc’s extended markdown |
| Tables | GitHub-style | Pandoc tables (more flexible) |
| Math | KaTeX (via shortcode/JS) | Built-in LaTeX rendering |
| Citations | Manual/custom | Native with CSL support |
| Code Execution | ❌ No | ✅ Yes (R, Python, Julia) |
| Div/Span | Attributes extension | Native .class {attr} |
| Callouts | Custom shortcodes | ::: {.callout} syntax |
| Cross-references | Custom/manual | [@fig:label] native |
| Diagrams | Mermaid (JS) | Native with Graphviz/Mermaid |
Migration Considerations from Quarto
Quarto callouts:
:::{.callout-warning}
This is a warning
:::Hugo equivalent (custom shortcode):
{{< callout type="warning" >}}
This is a warning
{{< /callout >}}Quarto figure cross-reference:
See @fig:architecture for details.
{#fig:architecture}Hugo equivalent:
See [Figure 1](#fig-architecture) for details.
 {#fig-architecture}💡 Best Practices for Learning Hub
Recommended Configuration
config.toml:
[markup]
defaultMarkdownHandler = "goldmark"
[markup.goldmark]
[markup.goldmark.renderer]
unsafe = true # Allow HTML for custom components
[markup.goldmark.parser]
autoHeadingID = true
autoHeadingIDType = "github" # Consistent with GitHub
[markup.goldmark.parser.attribute]
block = true
title = true
[markup.goldmark.extensions]
definitionList = true
footnote = true
linkify = true
strikethrough = true
table = true
taskList = true
typographer = true # Professional typography
[markup.highlight]
anchorLineNos = true
codeFences = true
guessSyntax = false
lineNumbersInTable = true
noClasses = false
style = "github"
tabWidth = 2Render Hooks Strategy
- Implement link hook - External link handling
- Implement image hook - Auto-optimization
- Implement heading hook - Linkable headings
- Implement code block hooks - Copy buttons, language-specific styling
Content Guidelines
Always specify language in code blocks:
```python <!-- Good --> ``` <!-- Bad: no syntax highlighting -->Use attributes for styling:
{.alert .alert-warning} Important note!Leverage footnotes for references:
Hugo uses Goldmark[^1] for parsing. [^1]: https://github.com/yuin/goldmarkUse tables for comparisons:
| Tool | Speed | Features | |------|-------|----------| | Hugo | ✅ Fast | Good |
🎯 Key Takeaways
- Goldmark is CommonMark-compliant - Predictable, standard behavior
- Extensive configuration options - Customize to your needs
- Render hooks provide powerful customization - Without changing content
- Chroma supports 100+ languages - Comprehensive syntax highlighting
- Extensions enable rich content - Tables, footnotes, task lists, etc.
🔜 Next Steps
Continue to Hugo vs Quarto Comparison to learn about:
- Detailed feature comparison
- Performance benchmarks with Learning Hub
- Migration complexity analysis
- When to choose each tool
- Hybrid approaches
📚 References
- Goldmark GitHub 📘 Official - Parser source code
- Hugo Markdown Configuration 📘 Official - Complete config reference
- CommonMark Spec 📘 Official - Markdown specification
- Chroma Styles Gallery 📗 Verified Community - Visual style preview
- Hugo Render Hooks 📘 Official - Custom rendering guide
- Syntax Highlighting 📘 Official - Chroma configuration