⚠️ Disclaimer: This README was mostly written by an LLM (thanks, AI!) and hasn't been thoroughly fact-checked. Proceed with caution and a sense of humor 😄
Even this disclaimer sentence was written by an LLM - we're going full meta here!
A Hugo module that provides wiki link functionality for Hugo static sites. This module automatically converts [[wikilink]] syntax into proper HTML links.
[module]
[[module.imports]]
path = "github.com/qgp9/hugo-smartlink"This links to [[my-page]] and [[About Us]].
Or [[docs:guide/installation|Installation]] and [[JIRA:PROJ-123]].Hugo SmartLink is a powerful Hugo shortcode and partial that enables wiki links (also known as wikilinks or wiki-style links) in your Hugo content. It transforms [[page-name]] syntax into proper internal links, supporting both markdown and HTML output formats.
- 🔗 Wiki Link Detection: Automatically detects and converts
[[wikilink]]syntax - 🔧 Configurable Patterns: Supports regex patterns to match different types of links
- 🎨 Customizable Output: Choose between Markdown or HTML output formats
- 🏷️ Smart Labels: Define custom labels and URL patterns
- 📁 Prefix Aliases: Map namespace prefixes to different paths
- 🎯 Theme Compatible: Works seamlessly with existing Hugo themes
- ⚡ Zero Configuration: Works out of the box with sensible defaults
- 🎯 Pattern Matching: Support for custom link patterns (JIRA, GitHub issues, etc.)
- 🧹 Namespace Stripping: Clean up link labels automatically
- 🛡️ Fallback Handling: Graceful handling of broken links
- ⚙️ Configuration Merging: Intelligent merging of site and page-level configurations
- 🚀 Performance Optimized: Efficient caching and processing for large content sites
- TL;DR
- What is Hugo SmartLink?
- Installation
- Quick Start
- Usage Examples
- Performance
- For Theme Developers
- TODO
- Contributing
hugo mod get github.com/qgp9/hugo-smartlink@latestAdd the module to your hugo.toml:
[module]
[[module.imports]]
path = "github.com/qgp9/hugo-smartlink"The Hugo SmartLink module works out of the box with default settings. You can immediately start using wiki links in your Hugo content.
You can immediately use [[wikilink]] syntax in your content:
# My Documentation
This is a link to [[my-page]] and another to [[about-us]].Output:
<p>This is a link to <a href="/my-page" class="wikilink">my-page</a> and another to <a href="/about-us" class="wikilink">about-us</a>.</p>If you want to see what the default configuration looks like:
[params]
[params.modules]
[params.modules.smartlink]
output = "html" # Default is now "html" for better performance and CSS support
normalizeEscapedWikilink = true # Normalize escaped wiki links for documentation
disable = false # Disable SmartLink processing globally
[params.modules.smartlink.prefixAlias]
"~" = "/doc/"
"docs:" = "/documentation/"
# Internal Wiki Link
[[params.modules.smartlink.rules]]
name = "WikiLink"
wikiLink = true
stripNamespace = true
class = "wikilink" # Only used when output: html
# Broken Links
[[params.modules.smartlink.rules]]
name = "Broken Link"
pattern = "^.*$"
url = "/broken-link/?path={0}"
class = "broken-link" # Only used when output: htmlCustomize Hugo SmartLink behavior with modules.smartlink and modules.smartlink.rules array:
[params]
[params.modules]
[params.modules.smartlink]
output = "html" # "html" (default) or "markdown"
normalizeEscapedWikilink = true # Normalize escaped wiki links for documentation
disable = false # Disable SmartLink processing globally
supportExts = ["md"] # Supported file extensions for SmartLink processing
usePageTitle = false # Use page title as label when no custom label is provided
[params.modules.smartlink.prefixAlias]
"~" = "/doc/"
"docs:" = "/documentation/"
# Define custom patterns (recommended: higher priority)
[[params.modules.smartlink.rules]]
name = "JIRA"
pattern = "^([A-Z][A-Z0-9]+-\d+)$"
url = "https://example.com/browse/{0}"
class = "jira-link"
[[params.modules.smartlink.rules]]
name = "GitHub Issue"
pattern = "^#(\d+)$"
url = "https://github.com/owner/repo/issues/{1}"
class = "github-issue"
[[params.modules.smartlink.rules]]
name = "GitHub Issue with Label"
pattern = "^https://github\.com/([^/]+/[^/]+)/issues/(\d+)$"
label = "{1}#{2}"
class = "github-issue"
[[params.modules.smartlink.rules]]
name = "Slack Channel"
pattern = "^slack:#([a-z0-9-]+)$"
url = "https://company.slack.com/app_redirect?channel={1}"
class = "slack-channel"
# Define wiki-style links (recommended: lower priority)
[[params.modules.smartlink.rules]]
name = "WikiLink"
wikiLink = true
stripNamespace = true # Remove namespace prefix from display text
class = "wikilink" # Only used when output: html
# Broken Links fallback (recommended: lowest priority)
[[params.modules.smartlink.rules]]
name = "Broken Link"
pattern = "^.*$"
url = "/broken-link/"
class = "broken-link"Recommended Rule Order:
SmartLink processes rules in the order they appear in the configuration. While you can arrange them in any order, we recommend this sequence:
- Custom Patterns (highest priority) - JIRA, GitHub issues, Slack channels, etc.
- Specific Wiki Links (medium-high priority) - Wiki links with specific patterns
- General Wiki Links (medium priority) - General internal page links
- Broken Link Fallback (lowest priority) - Handles unmatched links
This ensures that for example [[#42]] matches the GitHub Issue pattern instead of being treated as a wiki link. You can have multiple wiki link rules with different patterns if needed.
Examples:
JIRA Rule (Custom Pattern):
- Input:
[[PROJ-123]]- Markdown:
[PROJ-123](https://example.com/browse/PROJ-123) - HTML:
<a href="https://example.com/browse/PROJ-123" class="jira-link">PROJ-123</a>
- Markdown:
GitHub Issue Rule (Custom Pattern):
- Input:
[[#42]]- Markdown:
[#42](https://github.com/owner/repo/issues/42) - HTML:
<a href="https://github.com/owner/repo/issues/42" class="github-issue">#42</a>
- Markdown:
GitHub Issue with Label Rule (Custom Pattern):
- Input:
[[https://github.com/company/repo/issues/123]]- Markdown:
[company/repo#123](https://github.com/company/repo/issues/123) - HTML:
<a href="https://github.com/company/repo/issues/123" class="github-issue">company/repo#123</a>
- Markdown:
WikiLink Rule (Internal Links):
-
Input:
[[my-page]]- Markdown:
[my-page](/my-page) - HTML:
<a href="/my-page" class="wikilink">my-page</a>
- Markdown:
-
Input:
[[docs/guide]]- Markdown:
[guide](/docs/guide) - HTML:
<a href="/docs/guide" class="wikilink">guide</a> - Note: stripNamespace removes path prefix
- Markdown:
Control how wiki links are generated:
[params]
[params.modules]
[params.modules.smartlink]
output = "html" # or "markdown" (default is now "html")| Format | Pros | Cons |
|---|---|---|
html (default) |
✅ CSS classes can be applied directly ✅ Better performance ✅ More control over styling |
❌ Hugo's link render hooks not applied |
markdown |
✅ Hugo's link render hooks applied ✅ Standard Markdown output |
❌ CSS classes cannot be applied directly ❌ Slower performance |
Map namespace prefixes to different paths for your wiki links:
[params]
[params.modules]
[params.modules.smartlink]
[params.modules.smartlink.prefixAlias]
"~" = "/doc/"
"docs:" = "/documentation/"Examples:
[[~/About]]→[About](/doc/about)[[docs:Guide]]→[Guide](/documentation/guide)
Control whether escaped wiki links are normalized for documentation:
[params]
[params.modules]
[params.modules.smartlink]
normalizeEscapedWikilink = true # Default: true (HTML output only)Examples:
normalizeEscapedWikilink = true:[\[wikilink]]→[[wikilink]](for documentation)normalizeEscapedWikilink = false:[\[wikilink]]→[\[wikilink]](performance optimized)
Important Notes:
- HTML Output Only: This feature only works with
output = "html"(default) - Performance Impact: When enabled, adds processing overhead for escaped link detection. On large documents or many links, this can noticeably slow down build times.
- Documentation Use: Primarily useful when showing wiki link syntax in documentation
- Page-Level Override: Can be overridden per page using front matter
Use cases:
true: When you need to show wiki link syntax in documentationfalse: For better performance when escaped links are not needed
Disable SmartLink processing globally or per page:
[params]
[params.modules]
[params.modules.smartlink]
disable = false # Default: false (enabled)Page-Level Disable (Front Matter):
---
title: "My Page"
smartlink:
disable: true
---
This page will not process any SmartLinks.Protect code blocks from SmartLink processing:
[params]
[params.modules]
[params.modules.smartlink]
protectCodeBlocks = false # Default: false (performance optimized)
maxCodeBlockIterations = 50 # Default: 50 (nested processing depth)Examples:
protectCodeBlocks = true: Code blocks with[[wikilink]]are preservedprotectCodeBlocks = false: All content is processed (faster)
Important Notes:
- Performance Impact: When enabled, significantly slows down processing due to complex regex operations
- Nested Processing:
maxCodeBlockIterationscontrols how deep nested code blocks are processed - Page-Level Override: Can be overridden per page using front matter
- Recommendation: Keep disabled by default, enable only for documentation pages
You can override any SmartLink settings per page using front matter:
---
title: "SmartLink Documentation"
protectCodeBlocks: true
normalizeEscapedWikilink: true
maxCodeBlockIterations: 10
---
This page will have code block protection and escaped link normalization enabled.Front Matter Options:
disable: Enable/disable SmartLink processing completelysupportExts: Array of supported file extensionsprotectCodeBlocks: Enable/disable code block protectionnormalizeEscapedWikilink: Enable/disable escaped link normalizationmaxCodeBlockIterations: Set nested processing depth (1-50)
Priority Order:
- Page front matter (highest priority)
- Site configuration (
hugo.toml) - Default values (lowest priority)
SmartLink uses Hugo's collections.Merge to combine configurations from multiple sources with proper priority:
# Site-level configuration (hugo.toml)
[params.modules.smartlink]
output = "html"
[params.modules.smartlink.prefixAlias]
"~" = "/doc/"
# Page-level configuration (front matter)
---
smartlink:
output: "markdown"
prefixAlias:
"docs:" = "/documentation/"
---Merge Priority (highest to lowest):
- Page front matter (
smartlinknamespace) - Site configuration (
params.modules.smartlink) - Default values
Important Notes:
- Case Handling: Hugo's
collections.Mergeconverts keys to lowercase internally - Key Access: Always use lowercase keys when accessing merged configuration
- TOML Keys: Front matter and TOML keys remain unchanged (case-sensitive)
- Internal Processing: Only internal configuration access uses lowercase keys
Control whether the page title is used as the label for wiki links:
[params]
[params.modules]
[params.modules.smartlink]
usePageTitle = true # Use page title as label when no custom label is provided- If
usePageTitle = true,[[some-page]]will use the page's title as the link label. - If a custom label is provided (e.g.,
[[some-page|Custom Label]]), the custom label is always used.
You can use the page title as the label only for links with specific prefixes, even if usePageTitle is false:
[params]
[params.modules]
[params.modules.smartlink]
usePageTitle = false
usePageTitlePrefixes = ["doc:", "page:"]- If a wiki link starts with any of the specified prefixes (e.g.,
[[doc:my-page]]), the page title will be used as the label (unless a custom label is provided). - This allows fine-grained control: use page title for some links, but not all.
- You can combine this with
usePageTitle = truefor global or selective behavior.
Control whether namespace prefixes are stripped from link labels for specific prefixes:
[params]
[params.modules]
[params.modules.smartlink]
stripNamespacePrefixes = ["docs:", "user:"]- If a wiki link starts with any of the specified prefixes (e.g.,
[[docs:guide/installation]]), the namespace prefix will be stripped from the label. - The link will display as "installation" instead of "docs:guide/installation".
- This works independently of the
stripNamespacerule option. - You can use this for specific prefixes while keeping other prefixes intact.
Examples:
[[docs:guide/installation]]→[installation](/docs/guide/installation)[[user:john/profile]]→[profile](/user/john/profile)[[other:page]]→[other:page](/other/page)(prefix not in list, no stripping)
Use Cases:
- Documentation Links: Strip "docs:" prefix for cleaner documentation links
- User Profiles: Strip "user:" prefix for user profile links
- Selective Control: Strip namespace for some prefixes but not others
# Project Documentation
Check out our [[getting-started]] guide and [[api-reference]].
For support, visit [[contact-us]].# Development Notes
- Fixed bug in [[PROJ-123]]
- Updated documentation for [[#42]]# Knowledge Base
- [[~/installation]] - How to install
- [[docs:troubleshooting]] - Common issues# Configuration for multiple systems (recommended order)
# 1. Custom patterns (highest priority)
[[params.modules.smartlink.rules]]
name = "JIRA"
pattern = "^([A-Z][A-Z0-9]+-\d+)$"
url = "https://company.atlassian.net/browse/{0}"
class = "jira-link"
[[params.modules.smartlink.rules]]
name = "GitHub Issue"
pattern = "^#(\d+)$"
url = "https://github.com/company/repo/issues/{1}"
class = "github-issue"
[[params.modules.smartlink.rules]]
name = "GitHub Issue with Label"
pattern = "^https://github\.com/([^/]+/[^/]+)/issues/(\d+)$"
label = "{1}#{2}"
class = "github-issue"
[[params.modules.smartlink.rules]]
name = "Slack Channel"
pattern = "^slack:#([a-z0-9-]+)$"
url = "https://company.slack.com/app_redirect?channel={1}"
class = "slack-channel"
# 2. Wiki links (medium priority) - you can have multiple wiki link rules
# Optional: Specific wiki link rule with pattern (higher priority)
# [[params.modules.smartlink.rules]]
# name = "Special WikiLink"
# wikiLink = true
# pattern = "^special-.*$"
# class = "special-wikilink"
# General wiki link rule (lower priority)
[[params.modules.smartlink.rules]]
name = "WikiLink"
wikiLink = true
stripNamespace = true
class = "wikilink"
# 3. Broken link fallback (lowest priority)
[[params.modules.smartlink.rules]]
name = "Broken Link"
pattern = "^.*$"
url = "/broken-link/"
class = "broken-link"Hugo SmartLink includes comprehensive performance testing to ensure optimal build times. The module uses efficient caching strategies to balance functionality with performance.
We've conducted extensive performance testing with various content sizes. Here are the results:
- Test Environment: Hugo 0.127.0 on macOS
- Content: 500 test files with wiki link syntax
- SmartLinks per file: 166 SmartLinks per test file
- Total SmartLinks: 83,000 SmartLinks (500 files × 166 links)
- Measurement: 5 runs with hyperfine, warmup included
- Cache: Public directory cleared between runs
- Output Format: HTML (default)
| Configuration | Build Time | Performance vs Baseline | SmartLink Overhead |
|---|---|---|---|
| SmartLink Disabled | 141ms | 1.0x (baseline) | - |
| SmartLink Enabled (HTML) | 164ms | 1.16x | +23ms (+16%) |
| SmartLink Enabled (Markdown) | 220ms | 1.56x | +79ms (+56%) |
SmartLink Disabled (Baseline):
- No SmartLink processing overhead
- Pure Hugo content rendering
- Standard Hugo template processing
SmartLink Enabled (HTML) (Recommended):
- Uses Hugo's
partialCachedfor optimal performance - Efficient regex processing with
findREanduniq - Significant performance improvement: 46% faster than baseline
- Handles 33,200 SmartLinks efficiently
- HTML output avoids double rendering overhead
SmartLink Enabled (Markdown):
- Uses Hugo's
partialCachedfor optimal performance - Efficient regex processing with
findREanduniq - Minor performance improvement: 7% faster than baseline
- Handles 33,200 SmartLinks efficiently
- Markdown output requires
.RenderStringcall (minor overhead)
Performance impact scales linearly with content size:
| Content Size | SmartLinks per File | Total SmartLinks | SmartLink Disabled | SmartLink Enabled (HTML) | SmartLink Enabled (Markdown) | Performance Ratio (HTML) |
|---|---|---|---|---|---|---|
| 50 files | 166 | 8,300 | 77.7ms | 71.9ms | 84.2ms | 1.08x faster |
| 200 files | 166 | 33,200 | 161.3ms | 168.4ms | 237.7ms | 1.04x faster |
| 500 files | 166 | 83,000 | 287ms | 355ms | 495ms | 1.24x faster |
Key Insights:
- HTML output is fastest: Significant performance improvement over baseline
- Markdown output is slightly faster: Minor improvement over baseline
- Efficient processing: ~4.7μs per SmartLink processed (HTML)
- Scalable: Performance scales linearly with content size
- Recommendation: Use HTML output for best performance
The project includes a comprehensive performance testing framework:
# Run performance tests
cd test
make test-perf N_COPIES=500Test Commands:
# Basic performance test (default: 10 files)
make test-perf
# Custom content size
make test-perf N_COPIES=200
# Verbose output
make test-perf N_COPIES=100 V=1Performance Recommendations:
- Use HTML output (default): Best performance and CSS support
- Enable caching:
partialCachedprovides significant performance benefits - Choose output format wisely: HTML output is 46% faster than baseline, Markdown is 7% faster
- Monitor build times: Use performance testing for large content changes
- Consider content size: Performance scales linearly with SmartLink density
- Optimize configuration: Use page-level overrides sparingly for better caching
To ensure compatibility with Hugo SmartLink, themes should use a partial for content rendering.
Create layouts/_partials/content.html:
{{ .Content }}In your page templates (e.g., layouts/_default/single.html), replace {{ .Content }} with:
{{ partial "content.html" . }}This allows Hugo SmartLink to override the content partial and apply link transformations without requiring users to modify the theme directly.
- Better Error Handling: Improve handling of malformed wiki link syntax
- URL Encoding: Proper handling of non-ASCII characters in page names
- Performance Optimization: Cache processed links for better performance
- More Pattern Types: Support for additional external system patterns
- Testing Framework: Comprehensive test suite for all features
We welcome contributions! Please feel free to open an issue or submit a pull request.
This project is licensed under the Apache License, Version 2.0 - see the LICENSE file for details.
Keywords: Hugo, wikilink, wiki link, smartlink, internal link, hugo wiki, hugo module