Introduction#

Hugo Book is a documentation theme for Hugo. It provides a clean, readable layout for technical docs with minimal dependencies and no build tooling beyond Hugo itself.

Design Goals#

Minimalism over features.
The theme ships only what most documentation sites need: a sidebar menu, search, and a few shortcodes. There is no attempt to cover every use case. CSS is written by hand, not generated by a framework. JavaScript is optional for core navigation.
Theme as an extendable base.
Hugo Book is meant to be extended and adapted. ‘Inject’ partials, custom SCSS, and Hugo’s template override system let you change any part of the theme without modifying the source, while keeping the codebase intentionally small.
Long-term maintainability.
Few dependencies means fewer things to break. The theme avoids heavy JavaScript libraries, CSS frameworks, and complex build pipelines.

Get Started

Create a Site#

Prerequisites#

  • Hugo extended edition (required for SCSS processing)
  • Git (for theme installation using git submodules)
  • Go (for theme installation using Hugo Modules)

Quick Start#

Use the starter repository to get a working site in seconds. It can also be used as a GitHub template to create a new repository.

git clone https://github.com/alex-shpak/hugo-book-starter documentation
cd documentation
git submodule update --init --remote
hugo server --minify

By default, the theme renders pages under docs/ section as the sidebar menu. See Content Organisation for details.

Installation Methods#

The simplest approach. The theme is vendored directly into your repository.

git init
git submodule add https://github.com/alex-shpak/hugo-book themes/hugo-book

Set the theme in your config

theme = 'hugo-book'

To update the theme later

git submodule update --remote --merge

Uses Hugo Modules (requires Go).

Initialize your site as a module

hugo mod init github.com/user/my-docs

Add the theme import to your config

[module]
[[module.imports]]
  path = 'github.com/alex-shpak/hugo-book'

To update the theme later

hugo mod get -u

Download the theme from GitHub releases and extract it to themes/hugo-book.

Set the theme in your config

theme = 'hugo-book'

To update, download and extract the new release again.

Minimal Configuration#

baseURL = 'https://example.com/'
title = 'My Documentation'
theme = 'hugo-book'

# Required for mermaid and katex shortcodes
[markup.goldmark.renderer]
  unsafe = true

See Configuration for the full list of parameters.

Configuration#

All theme parameters are set under [params] in your site config. Every parameter is optional.

Theme Parameters#

[params]
  # Color theme: 'light', 'dark' or 'auto'
  # Auto switches based on OS/browser preference
  BookTheme = 'light'

  # Show table of contents on right side of pages
  # Can also be set per-page via frontmatter
  BookToC = true

  # Path to favicon file relative to 'static' directory
  BookFavicon = 'favicon.png'

  # Path to logo image file relative to 'static' directory
  BookLogo = 'logo.png'

  # Root section to render as sidebar menu
  # Default: 'docs'
  BookSection = 'docs'

  # Repository URL, used for edit and commit links
  BookRepo = 'https://github.com/user/repo'

  # Template for "Last Modified" commit link in page footer
  # Requires enableGitInfo = true in site config
  # Available context: .Site, .Page, .GitInfo
  BookLastChangeLink = '{{ .Site.Params.BookRepo }}/commit/{{ .GitInfo.Hash }}'

  # Template for "Edit this page" link in page footer
  # Available context: .Site, .Page, .Path
  BookEditLink = '{{ .Site.Params.BookRepo }}/edit/main/{{ .Path }}'

  # Date format used in git info and blog posts
  BookDateFormat = 'January 2, 2006'

  # Enable full-text search with fuse.js
  BookSearch = true

  # Enable comments template on pages
  # By default uses Disqus; override partials/docs/comments.html for others
  BookComments = true

  # /!\ Experimental, may change or be removed.
  # Enable portable markdown links to resolve relative .md links to Hugo URLs.
  # Lets you write [text](./other.md) instead of Hugo's relref shortcode.
  #   false     - disabled, relative .md links not resolved
  #   'warning' - enabled, prints a build warning if linked page doesn't exist
  #   'error'   - enabled, fails the build if linked page doesn't exist
  BookPortableLinks = false

  # /!\ Experimental, may change or be removed.
  # Register a service worker for offline access to visited pages.
  #   false      - disabled (default)
  #   true       - caches pages as you visit them
  #   'precache' - pre-populates cache with all site pages on first load
  BookServiceWorker = false

  # Only show languages that have translations for current page
  BookTranslatedOnly = false

Full-text search is enabled by default using Fuse.js. The search index is built at page load from a generated JSON file.

[params]
  BookSearch = true

To exclude a page from the search index, set bookSearchExclude: true in its frontmatter.

If search is not working, verify that baseURL in your config matches the URL where the site is hosted. A mismatch prevents the search index from loading.

Hugo Site Configuration#

These Hugo settings are relevant for the theme

# Preserve URL casing (recommended)
disablePathToLower = true

# Enable git metadata for "Last Modified" footer
enableGitInfo = true

Markup#

Goldmark Renderer#

The unsafe option is required for Mermaid and KaTeX shortcodes to render correctly

[markup.goldmark.renderer]
  unsafe = true

Table of Contents#

Control the heading levels included in the table of contents

[markup.tableOfContents]
  startLevel = 1
  endLevel = 4

The startLevel and endLevel values apply globally. Individual pages can toggle the ToC on or off with the bookToC frontmatter parameter.

Output Formats#

The theme supports plain text output alongside HTML, useful for accessibility and LLMs

[outputFormats.txt]
  mediaType = 'text/plain'
  baseName = 'source'
  isPlainText = true

[outputs]
  home = ['html', 'txt', 'rss']
  page = ['html', 'txt']
  section = ['html', 'txt']

Content Organisation#

Hugo Book renders pages from a section as a sidebar menu. By default this is the docs/ directory.

Example Directory Structure#

content/
├── docs/
│   ├── _index.md
│   ├── getting-started.md
│   ├── guide/
│   │   ├── _index.md
│   │   ├── install.md
│   │   └── configure.md
│   └── reference/
│       └── _index.md
├── posts/
│   ├── _index.md
│   └── my-post.md
└── _index.md

Pages are ordered by weight frontmatter, then alphabetically. Section _index.md files define the section entry in the menu.

Changing the Menu Section#

By default, pages under docs/ are rendered as the sidebar menu. Change this with the BookSection parameter in Configuration. Set to '/' to render all top-level sections.

Page Layouts#

The theme provides several layouts:

LayoutUsageDescription
(default)Documentation pagesSidebar menu + content + optional ToC
landinglayout: landingFull-width, no sidebar. Used for homepages.
booklayout: bookSingle-page view with all subsections listed
postsPages under posts/Blog-style with date, tags, pagination

Set the layout in frontmatter

---
layout: landing
---

See Pages for frontmatter reference and Menus for navigation controls.

Page Frontmatter#

Every page can use these frontmatter parameters to control its behavior in the theme.

---
title: My Page
weight: 10
bookToC: true
bookCollapseSection: true
---
+++
title = 'My Page'
weight = 10
bookToC = true
bookCollapseSection = true
+++
{
  "title": "My Page",
  "weight": 10,
  "bookToC": true,
  "bookCollapseSection": true
}

These parameters control how the page appears in the sidebar menu. Set in page frontmatter.

ParameterDefaultDescription
weightMenu ordering. Lower values appear first. Without weight, pages are sorted alphabetically.
bookHiddenfalseHide the page from the sidebar menu. The page is still accessible by URL.
bookCollapseSectionfalseMake a section collapsible in the sidebar. Subsections are hidden until clicked.
bookFlatSectionfalseDisplay subsection pages at the same level instead of nesting them.
bookHrefOverride the menu link with an external URL.
bookIconDisplay an icon next to the menu entry.

Content Display#

These parameters control page content rendering. Set in page frontmatter.

ParameterDefaultDescription
bookToCShow or hide the table of contents. Overrides the site-level BookToC setting.
bookCommentsShow or hide comments. Overrides the site-level BookComments setting.
bookSearchExcludefalseExclude this page from the search index.

See Blog for post-specific frontmatter.

Blog#

Hugo Book includes templates for blog-style posts with dates, tags, and pagination. Blog functionality is very basic.

Setup#

Create a posts/ section in your content directory

content/
└── posts/
    ├── _index.md
    └── my-first-post.md

Add the blog section to a menu so readers can find it

---
title: Blog
menu:
  after:
    weight: 5
---

Post Frontmatter#

---
title: "My First Post"
date: 2025-01-15
tags: ["hugo", "documentation"]
categories: ["Guides"]
---

Thumbnails#

Posts can display a thumbnail image. By default the theme looks for a file matching thumbnail.* in the post’s page bundle. Override the pattern per-post

---
bookPostThumbnail: "cover.*"
---

Date Format#

The date display format is configured site-wide via BookDateFormat. See Configuration for details.

Multi-Language Support#

Hugo Book supports Hugo’s multilingual mode with content translation by directory.

Configuration#

Define languages in your site config

[languages]
[languages.en]
  label = 'English'
  contentDir = 'content.en'
  weight = 1

[languages.zh]
  label = 'Chinese'
  contentDir = 'content.zh'
  weight = 2

[languages.he]
  label = 'Hebrew'
  contentDir = 'content.he'
  direction = 'rtl'
  weight = 3

A language selector dropdown appears automatically when multiple languages are configured.

RTL Support#

Set direction = 'rtl' on a language to enable right-to-left layout. The entire page layout, including the sidebar menu, mirrors automatically.

Translation Dropdown#

By default, all configured languages appear in the selector. To only show languages that have a translation for the current page

[params]
  BookTranslatedOnly = true

Shortcodes#

Hugo Book includes shortcodes for common documentation patterns. All interactive shortcodes work without JavaScript using CSS-only techniques.

Hugo shortcodes use two delimiter styles:

{{< shortcode >}}
Renders inner content as plain HTML. Use for shortcodes that don’t contain markdown.
{{% shortcode %}}
Processes inner content as markdown. Use when the shortcode body contains markdown formatting.

Use {{% shortcode %}} when the body contains markdown that needs to be rendered. {{< shortcode >}} passes content as-is without markdown processing. When nesting shortcodes (e.g. tabs inside columns), the outer shortcode must use {{% shortcode %}} for the inner shortcodes and markdown to be processed.

Asciinema#

Embed terminal recordings with the Asciinema player.

Syntax#

{{< asciinema
  cast="recording.cast"
  loop=true
  autoplay=true
  speed=2 >}}

The cast parameter accepts page resources, site resources, or remote URLs.

Example#

Parameters#

cast
Path to the .cast file. Can be a local resource or a remote URL.

All other parameters are passed directly to the Asciinema player. See the Asciinema player options for the full list.

Common options: autoplay, loop, speed, theme, cols, rows, idleTimeLimit, preload.

Buttons#

Styled links that can point to local pages or external URLs. External links automatically open in a new tab.

Syntax#

{{< button relref="/" [class="..."] >}}Get Home{{< /button >}}
{{< button href="https://github.com/alex-shpak/hugo-book" >}}Github{{< /button >}}

Example#

Home Github

Parameters#

href
URL for external links.
relref
Hugo page reference for internal links.
class
Additional CSS classes.

Columns#

Organize content horizontally. Renders a markdown list as up to 3 side-by-side columns.

Syntax#

{{% columns [ratio="1:1"] [class="..."] %}}
- ### Left
  Content...

- ### Right
  Content...
{{% /columns %}}

Example#

  • File-Tree Menu#

    The sidebar menu is automatically generated from your content directory structure. Pages are ordered by weight frontmatter.

  • Hugo Menus#

    Additional menu entries can be added above or below the file-tree using Hugo’s standard menu system in your site config.

  • Landing Menu#

    Pages with layout: landing use a separate menu defined under menu.home for header navigation.

Custom Ratio#

Set relative column widths with the ratio parameter

{{% columns ratio="1:2" %}}
- ### Sidebar
- ### Content Area
{{% /columns %}}
  • File-Tree Menu#

    The sidebar menu is automatically generated from your content directory structure. Pages are ordered by weight frontmatter.

  • Hugo Menus#

    Additional menu entries can be added above or below the file-tree using Hugo’s standard menu system in your site config.

Details#

Collapsible content using the HTML5 <details> element.

Syntax#

Positional arguments:

{{% details "Title" [open] %}}
Markdown content
{{% /details %}}

Named parameters:

{{% details title="Title" open=true %}}
Markdown content
{{% /details %}}

Example#

What Hugo version is required?

Hugo Book requires Hugo {x} or later, extended edition. The extended edition is needed for SCSS processing.

How do I override the theme?

Create matching files in your project’s layouts/ or assets/ directory. Hugo’s lookup order will use your files over the theme’s.

Parameters#

title (or first positional argument)
The summary text shown when collapsed.
open (or second positional argument)
Start expanded. Default: collapsed.

Images#

Enhanced image display with click-to-expand behavior.

Syntax#

{{< image src="photo.jpg" alt="Description" title="Caption" loading="lazy" >}}

Example#

Parameters#

src
Path to the image. Supports page resources, site resources, and URLs.
alt
Alternate text for accessibility.
title
Caption displayed below the image.
loading
Loading strategy: lazy, eager, or auto.
class
Additional CSS classes on the <img> element.

OpenAPI#

Renders an OpenAPI 3.x specification as a documentation page. Operations are grouped by their first tag.

Syntax#

{{% openapi src="spec.json" %}}

The spec file is loaded via resources.Get, so the path is relative to the site’s assets/ directory. JSON and YAML are both supported.

Example#

This API allows writing down marks on a Tic Tac Toe board and requesting the state of the board or of individual squares.

v1.0.0

Security#

app2AppOauth
oauth2
Basic HTTP Authentication
basicHttpAuthentication
http Basic
Bearer token using a JWT
bearerHttpAuthentication
http Bearer JWT
API key provided in console
defaultApiKey
apiKey in header (api-key)
user2AppOauth
oauth2

Gameplay#

Get the whole board#

Retrieves the current state of the board and the winner.

Request (requires defaultApiKey, app2AppOauth)#

GET /board

Responses#

200: OK

Schema: status

PropertyTypeRequiredDescription
boardboardno
winnerwinnernoWinner of the game. . means nobody has won yet.

Get a single board square#

Retrieves the requested square.

Request (requires bearerHttpAuthentication, user2AppOauth)#

GET /board/{row}/{column}

Path parameters#

Parameter nameValueDescriptionAdditional
rowcoordinateBoard row (vertical coordinate)Required, Example: 1
columncoordinateBoard column (horizontal coordinate)Required, Example: 1

Responses#

200: OK

Schema: mark

400: The provided parameters are incorrect

"Illegal coordinates"

Schema: errorMessage

Set a single board square#

Places a mark on the board and retrieves the whole board and the winner (if any).

Request (requires bearerHttpAuthentication, user2AppOauth)#

PUT /board/{row}/{column}

Path parameters#

Parameter nameValueDescriptionAdditional
rowcoordinateBoard row (vertical coordinate)Required, Example: 1
columncoordinateBoard column (horizontal coordinate)Required, Example: 1

Request body (required)#

Schema: mark

Responses#

200: OK

Schema: status

PropertyTypeRequiredDescription
boardboardno
winnerwinnernoWinner of the game. . means nobody has won yet.

400: The provided parameters are incorrect

illegalCoordinates

"Illegal coordinates."

invalidMark

"Invalid Mark (X or O)."

notEmpty

"Square is not empty."

Schema: errorMessage

Models#

board#

Type: array of array of mark

coordinate#

Type: integer

errorMessage#

A text message describing an error

Type: string

mark#

Possible values for a board square. . means empty square.

Type: string (enum: ., X, O)

status#

PropertyTypeRequiredDescription
boardboardno
winnerwinnernoWinner of the game. . means nobody has won yet.

winner#

Winner of the game. . means nobody has won yet.

Type: string (enum: ., X, O)

Hints#

Callout blocks for notes, warnings, and other contextual messages. Also supports standard GitHub markdown alerts.

Syntax#

{{% hint [info|success|warning|danger] %}}
Markdown content
{{% /hint %}}

Or using markdown alerts

> [!NOTE|TIP|IMPORTANT|WARNING|CAUTION]
> Markdown content

Example#

Default hint
Without a specified type.

Info
Use for supplementary information that helps the reader.

Success
Use to highlight a recommended approach or positive outcome.

Warning
Use for important caveats or potential issues.

Danger
Use for critical warnings about breaking changes or data loss.

Markdown Alerts#

Standard GitHub markdown alert syntax is also supported:

The theme requires Hugo extended edition for SCSS processing.

Set disablePathToLower = true in your config to preserve URL casing.

The unsafe = true goldmark setting is required for Mermaid and KaTeX shortcodes.

Service worker support is experimental and may change in future releases.

Enabling BookPortableLinks = 'error' will fail the build if any markdown link targets are missing.

Mermaid#

Render diagrams and charts with Mermaid. The library is loaded automatically on first use.

Override Mermaid initialization by creating assets/mermaid.json in your project.

Syntax#

Use fenced code blocks (recommended) or the shortcode

```mermaid
graph LR
    A --> B
```
{{< mermaid [class="..."] >}}
graph LR
    A --> B
{{< /mermaid >}}

Examples#

  • flowchart TD
        A[Content Files] --> B[Hugo Build]
        B --> C[HTML Output]
        B --> D[Search Index]
        B --> E[RSS Feed]
    sequenceDiagram
        Browser->>Hugo: Request page
        Hugo->>Theme: Apply template
        Theme->>Browser: Rendered HTML
        Browser->>Browser: Load shortcodes
  • pie title Theme Assets
        "SCSS" : 8
        "HTML Templates" : 30
        "JavaScript" : 3
        "i18n Files" : 35
    gitGraph
       commit id: "init"
       commit id: "add theme"
       branch feature
       checkout feature
       commit id: "add docs"
       commit id: "add config"
       checkout main
       merge feature
       commit id: "deploy"

Explore more diagram types on the Mermaid documentation.

Steps#

Render an ordered list as a series of numbered steps with visual connectors.

Syntax#

{{% steps %}}
1. ## Step Title
   Step description...

2. ## Step Title
   Step description...
{{% /steps %}}

Example#

  1. Create your site#

    Run hugo new site my-docs to scaffold a new Hugo project.

  2. Add the theme#

    Clone or add hugo-book as a submodule in your themes/ directory.

  3. Write content#

    Add markdown files under content/docs/. Each file becomes a page in the sidebar menu.

  4. Deploy#

    Run hugo to build the site. The output is in the public/ directory, ready for any static hosting.

Tabs#

Organize content by context, for example installation instructions for each supported platform.

Syntax#

{{< tabs >}}
{{% tab "First" %}} Markdown content {{% /tab %}}
{{% tab "Second" %}} Markdown content {{% /tab %}}
{{< /tabs >}}

Example#

brew install hugo
sudo snap install hugo
choco install hugo-extended

KaTeX#

Render math typesetting with KaTeX. The library is loaded automatically on first use.

Activation#

KaTeX is activated on the page by the first use of the shortcode or a katex code block. You can force activation with {{< katex />}}, then use delimiters anywhere on the page.

Block Rendering#

Three equivalent ways to render display math:

  • Shortcode

    {{< katex display=true >}}
    f(x) = \int_{-\infty}^\infty
    \hat f(\xi)\,e^{2 \pi i \xi x}\,d\xi
    {{< /katex >}}
  • Code block

    ```katex
    f(x) = \int_{-\infty}^\infty
    \hat f(\xi)\,e^{2 \pi i \xi x}\,d\xi
    ```
  • Dollar delimiters

    $$
    f(x) = \int_{-\infty}^\infty
    \hat f(\xi)\,e^{2 \pi i \xi x}\,d\xi
    $$

Result:

$$ f(x) = \int_{-\infty}^\infty\hat f(\xi),e^{2 \pi i \xi x},d\xi $$

Inline Rendering#

SyntaxOutput
{{< katex >}}\pi(x){{< /katex >}}\( \pi(x) \)
\\( \pi(x) \\)\( \pi(x) \)

Configuration#

Override KaTeX options by creating assets/katex.json. For example, to enable $...$ inline delimiters

{
  "delimiters": [
    {"left": "$$", "right": "$$", "display": true},
    {"left": "$", "right": "$", "display": false},
    {"left": "\\(", "right": "\\)", "display": false},
    {"left": "\\[", "right": "\\]", "display": true}
  ]
}

See KaTeX options for all available settings.

Styles#

Built-in Themes#

Set BookTheme in your site config

[params]
  BookTheme = 'light'  # or 'dark', 'auto'
ThemeDescription
lightDefault. Light background with dark text.
darkDark background (Nord palette) with light text.
autoSwitches between light and dark based on OS/browser preference (prefers-color-scheme).

Plugin Themes#

Additional themes are available as SCSS plugins. Import in your assets/_custom.scss

@import "plugins/themes";

Then set BookTheme to one of:

ThemeDescription
contrast-lightHigh contrast light variant
contrast-darkHigh contrast dark variant
contrast-autoHigh contrast, auto-switching
catppuccin-lightCatppuccin Latte palette
catppuccin-darkCatppuccin Frappe palette
catppuccin-autoCatppuccin, auto-switching
ayu-lightAyu light palette
ayu-darkAyu mirage dark palette
ayu-autoAyu, auto-switching

Custom Theme#

Define a theme mixin in assets/_custom.scss

@mixin theme-mytheme {
  --body-background: #fafafa;
  --body-font-color: #333;
  --color-link: #0066cc;
  --color-visited-link: #6633cc;
}

Then set BookTheme = 'mytheme'. The theme uses @include theme-{{ .Site.Params.BookTheme }} to select the mixin at build time.

For auto-switching themes (light/dark based on OS preference), see the theme-auto mixin in assets/_defaults.scss as a reference.

CSS Custom Properties#

Override CSS custom properties in _custom.scss to adjust the current theme. The full set of properties is defined in assets/_defaults.scss.

Custom Styles#

The theme includes an empty assets/_custom.scss file that is loaded after all theme styles. Create this file in your project’s assets/ directory to add custom CSS without overriding the entire stylesheet.

// assets/_custom.scss

.book-page {
  max-width: 60rem;
}

.book-menu nav {
  font-size: 0.9rem;
}

SCSS Plugins#

The theme ships with optional SCSS plugins. Import them in your _custom.scss:

@import "plugins/numbered"; /* Automatically number headings in the content area */
@import "plugins/scrollbars"; /* Style scrollbars in the sidebar and content area */

SCSS Variables#

Layout variables like sidebar width, breakpoints, and padding are defined in assets/_defaults.scss. Override them in your _custom.scss.

Override Priority#

Hugo’s lookup order lets you override any theme file by creating the same file in your project:

  • layouts/: Override templates and partials
  • assets/: Override SCSS and JavaScript
  • static/: Override static files (favicon, images)
  • i18n/: Override or add translations

Inject Partials#

The theme provides empty partial templates at key points in the page layout. Override these to inject custom HTML without modifying the base templates.

Create matching files in your project’s layouts/_partials/docs/inject/ directory.

PartialLocation
inject/head.htmlInside <head>, e.g. meta tags, stylesheets, scripts
inject/body.htmlBefore </body>, e.g. analytics, chat widgets, scripts
inject/menu-before.htmlBefore the sidebar menu
inject/menu-after.htmlAfter the sidebar menu
inject/content-before.htmlBefore the page content
inject/content-after.htmlAfter the page content
inject/toc-before.htmlBefore the table of contents
inject/toc-after.htmlAfter the table of contents
inject/footer.htmlInside the page footer

Example#

To add a Google Analytics script, create layouts/_partials/docs/inject/head.html

{{ with .Site.Params.googleAnalytics }}
<script async src="https://www.googletagmanager.com/gtag/js?id={{ . }}"></script>
{{ end }}

To add a custom banner above every page, create layouts/_partials/docs/inject/content-before.html

<div class="my-banner">
  This documentation is for version 2.0. See <a href="/v1/">version 1.0 docs</a>.
</div>