
Preface
Due to unfamiliarity with frontend development, considerable time was spent exploring and trial-and-error during the implementation of the Docs section of this site, and the current solution still has some issues. This article aims to review the technical selection and implementation details of this process, sharing a feasible solution based on Hugo + HTMX. Although this solution is still under continuous iteration, it is hoped to provide new ideas for developers seeking similar solutions.
When building a technical documentation site, a trade-off is usually required between “static generation performance” and “Single Page Application (SPA) interactive experience”. As a high-performance static site generator, Hugo can provide extremely fast first-screen loading speeds and perfect SEO support, but its default page transition method is traditional full-page browser refresh. This refresh method causes the sidebar scrollbar to reset and the page to flicker, fragmenting the user’s reading experience.
To address this issue, this solution utilizes Hugo’s nestable section structure and powerful template engine, combined with the asynchronous request capabilities of HTMX. Through an architecture of “static generation + dynamic enhancement”, a documentation system is implemented that retains the advantages of a static site while possessing a smooth experience similar to Docusaurus or GitBook. This article will detail the design ideas and implementation details of the documentation section.
What is HTMX?
HTMX is a lightweight JavaScript library that allows developers to declaratively initiate AJAX requests, handle WebSockets, or Server Sent Events directly within HTML tags using extended attributes (such as hx-get, hx-target, hx-swap).
The core concept of HTMX is HTML Over the Wire. That is, the server directly returns HTML fragments, and the frontend is only responsible for replacing these fragments into the specified position on the page. This pattern is highly compatible with Hugo’s template rendering mechanism: Hugo is responsible for generating HTML on the server side (at build time), while HTMX is responsible for dynamically updating HTML on the client side (at runtime).
Core Architecture: Three-Column Responsive Layout
The core requirement of a documentation page is efficient information acquisition and navigation. Therefore, this solution adopts a classic “Left Navigation - Center Content - Right Directory” three-column layout design. In terms of layout implementation, a flexible container is built using CSS Flexbox. To adapt to screen devices of different sizes, precise responsive breakpoint logic is defined:
- Mobile and Tablet: Hide left and right sidebars, the content area occupies the screen exclusively, and navigation is invoked via a hamburger menu.
- Desktop: Display the left navigation bar, and the content area adapts to the remaining space.
- Widescreen Devices: Display the complete three-column structure, with the right directory column (TOC) fixed.
This design ensures the best visual experience whether quickly consulting on a mobile phone or reading in depth on a widescreen monitor.
Data-Driven: YAML-Based Navigation System
The directory structure of technical documentation often has characteristics of multi-level nesting and strict order. To achieve precise control over the document structure, this solution abandons Hugo’s default sorting method based on file weight (Weight) or date, and instead adopts a YAML Data-Driven strategy.
1. Single Data Source
For each document that is a separate Section and located in the root directory, a nav.yaml file is maintained. This file defines all menu items, hierarchical relationships, and jump links under that chapter.
# nav.yaml example
- title: "Quick Start"
url: "/docs/start/"
- title: "Core Concepts"
children:
- title: "Architecture Design"
url: "/docs/concepts/architecture/"
- title: "Data Flow"
url: "/docs/concepts/data-flow/"
This method decouples “document structure” from “file storage”, allowing developers to flexibly adjust the display order and hierarchy of documents without renaming files or modifying Frontmatter.
2. Server-Side Rendering: Recursive Sidebar
During the server-side build phase, the Hugo template reads the nav.yaml file and renders it as an HTML sidebar through a Recursive Partial Template.
The template traverses the YAML data:
- Renders the link and title of the current node.
- If the node contains
children, it recursively calls itself. - Combined with accordion interaction logic, deep-level menu folding and unfolding are implemented.
The core recursive logic is shown below (pseudocode):
{{ $navData := .navData }}
<!-- Traverse navigation data -->
{{ range $navData }}
<li>
{{ if .children }}
<!-- If there are child nodes, render the collapse button and recursively call itself -->
<span>{{ .title }}</span>
<ul>
<!-- Recursive call: pass child node data -->
{{ partial "recursive-template.html" (dict "navData" .children) }}
</ul>
{{ else }}
<!-- If it is a leaf node, render the HTMX link -->
<a href="{{ .url }}"
hx-get="{{ .url }}"
hx-target="#docs-content-target"
hx-push-url="true">
{{ .title }}
</a>
{{ end }}
</li>
{{ end }}
3. Client-Side Calculation: Previous/Next Page
This is a highlight design of this solution. Usually, Hugo’s .Prev and .Next variables are generated based on the global context, making it difficult to strictly follow the tree structure of the sidebar.
To solve this problem, navigation logic is implemented on the client side using JavaScript:
- Asynchronous Data Fetching: When the page loads, the JS script requests the
nav.yamlfile of the current chapter viafetch. - Parsing and Flattening: Parse the YAML data in the browser and flatten it into a linear array.
- Positioning and Generation: Find the index in the array based on the URL of the current page, thereby precisely calculating the target links for “Previous Page” and “Next Page”.
This method ensures that the bottom navigation buttons always maintain strict consistency with the visual order of the sidebar, whether the user jumps via a link or accesses a deep page directly.
Interaction Enhancement: Deep Integration of HTMX
This is the most critical dynamic enhancement part of this solution. By transforming traditional hyperlinks into asynchronous requests using HTMX, page switching without refresh is achieved.
1. Declarative Navigation
On the link elements of the sidebar, the following HTMX attributes are added:
hx-get: Specifies the target URL of the link, telling the browser to initiate a GET request via AJAX.hx-target: Specifies which container in the page the HTML content returned by the server should replace. Here, it is specified as the middle “main content area”, thereby keeping the header navigation and sidebar from being re-rendered.hx-push-url: Instructs HTMX to use the History API to update the browser’s address bar URL, ensuring that the browser’s “Back” and “Forward” buttons still work normally, and the user stays on the current page after refreshing.
2. Lifecycle and Plugin Reloading
After introducing HTMX, the page no longer triggers the standard DOMContentLoaded event, which causes third-party scripts relying on this event (such as code highlighting Prism.js, mathematical formula rendering KaTeX, image lightbox MediumZoom, etc.) to fail after page switching.
To solve this problem, the lifecycle event hooks provided by HTMX are utilized. Listen for the htmx:afterSwap event, which is triggered after the HTML content is replaced and inserted into the DOM. In the callback function, the following operations are performed:
- Reset Scrollbar: Force the window scroll position to reset to the top to prevent staying at the reading progress position of the previous page after page switching.
- Reinitialize Plugins: Manually call the initialization functions of various UI components to ensure that code block highlighting, copy button functionality, and image click-to-zoom effects remain effective in the newly loaded content.
document.body.addEventListener('htmx:afterSwap', function(evt) {
// Ensure only document content area updates are handled
if (evt.detail.target.id === 'docs-content-target') {
// 1. Reinitialize TOC highlighting and scroll listening
if (window.DocsTOC) {
window.DocsTOC.initHighlighting();
window.DocsTOC.initSmoothScroll();
}
// 2. Rebind code block copy buttons
if (window.DocsUtilities) {
window.DocsUtilities.initCodeCopy();
}
// 3. Update sidebar active state
if (window.DocsSidebar) {
window.DocsSidebar.updateActiveState(window.location.pathname);
}
}
});
Right Directory: Intelligent Awareness and Sticky Positioning
The directory column on the right is mainly used to display the outline structure of the current page. To improve the reading experience, two key optimizations were made.
1. Sticky Positioning
Through the CSS position: sticky property, the right directory column is fixed to the right side of the viewport. When the user scrolls down to read a long article, the directory column remains visible, facilitating jumps to other chapters at any time.
2. Scroll Listening
To let the user clearly know which chapter is currently being read, an automatic highlighting mechanism based on scroll position is implemented.
The script calculates the position of heading elements (H2, H3) in the main text relative to the viewport in real time. A trigger threshold is set (e.g., 55% of the viewport height). When a heading scrolls past this threshold line, the script determines that the chapter is the “current active chapter” and adds an active style (Active Class) to the corresponding link in the TOC. This real-time visual feedback greatly enhances the reading experience of long documents.
Shortcomings and Advantages
Shortcomings
- State Persistence: Currently,
localStorageorsessionStorageis not used to cache the folding state of the sidebar. Therefore, every time the page is forced to refresh, the sidebar will revert to the default state, potentially showing an unfolding animation effect. - Parent Node Interaction: Parent nodes in the navigation exist only as folding containers and do not support clicking to jump to an Overview Page, which limits the flexibility of the information architecture to some extent.
Advantages
- Pure Static Architecture: Retains the characteristics of static sites being easy to deploy, highly secure, and requiring no backend maintenance.
- Ecosystem Reuse: Perfectly reuses Hugo’s existing template engine, Shortcodes, and content management system, without needing to reconstruct the entire build process for interactive experience.
Summary
- For Machines (Search Engines): It is a standard static website with a complete HTML structure that is easy to index.
- For Users: It behaves like a Single Page Application (SPA), with rapid page switching, no white screen, and no flickering.
This solution fully utilizes Hugo’s advantages in content management and build speed, while leveraging HTMX to complement the shortcomings of static sites in interactive experience with extremely low complexity.