Hugo Theme Setup and Customization
Installing a Hugo theme is simple. Customizing it properly — there are nuances. Main rule: never edit files inside themes/. Any changes in theme directory get overwritten on update. Hugo provides override mechanism through root folders.
Connecting Theme
Method 1: Git submodule (recommended)
git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke themes/ananke
In hugo.toml:
theme = "ananke"
When cloning repository:
git clone --recurse-submodules https://github.com/yourorg/yoursite
Method 2: Hugo Modules (modern approach)
# hugo.toml
[module]
[[module.imports]]
path = "github.com/theNewDynamic/gohugo-theme-ananke"
hugo mod init github.com/yourorg/yoursite
hugo mod get github.com/theNewDynamic/gohugo-theme-ananke
Hugo Modules work through Go modules proxy, versioned via go.sum.
Override Mechanism
Hugo searches files by priority: root project folders → theme folders. This means file layouts/partials/header.html in root completely replaces themes/themename/layouts/partials/header.html.
myproject/
├── layouts/
│ └── partials/
│ └── header.html ← this file is used
└── themes/
└── mytheme/
└── layouts/
└── partials/
└── header.html ← this is ignored
To customize part of template: copy file from theme to root layouts/, then edit copy.
Theme Parameters via hugo.toml
Most themes read settings from [params] section. Theme documentation lists available parameters:
[params]
logo = "/images/logo.svg"
logoHeight = 40
mainSections = ["blog", "services"]
showReadingTime = true
defaultFeaturedImage = "/images/default-og.jpg"
googleFonts = "Montserrat:300,400,600"
footerText = "© 2024 Company. All rights reserved."
[params.social]
twitter = "yourhandle"
linkedin = "company/yourcompany"
github = "yourorg"
[params.contact]
email = "[email protected]"
phone = "+1 (xxx) xxx-xxxx"
Style Override
Themes usually provide entry point for custom styles. Two patterns:
Pattern 1: Custom CSS file
Theme reads params.customCSS variable:
[params]
customCSS = ["/css/custom.css"]
File static/css/custom.css adds to theme styles.
Pattern 2: Override SCSS variables
If theme uses SCSS with variables, create assets/sass/_variables_override.scss:
// Override theme
$primary-color: #2563eb;
$font-family-base: 'Inter', sans-serif;
$border-radius-base: 4px;
$container-max-width: 1280px;
In override file assets/sass/main.scss:
@import "variables_override";
@import "../../../themes/mytheme/assets/sass/main"; // path to theme
@import "custom";
Navigation Customization
Menu in Hugo configured via config, not theme:
[[menus.main]]
name = "Home"
url = "/"
weight = 1
[[menus.main]]
name = "Services"
url = "/services/"
weight = 2
[menus.main.params]
icon = "briefcase"
[[menus.main]]
name = "Blog"
url = "/blog/"
weight = 3
[[menus.footer]]
name = "Privacy Policy"
url = "/privacy/"
weight = 1
Themes render {{ range .Site.Menus.main }} — no need to override navigation partial unless custom markup needed.
Partial Template Override
Example: theme has layouts/partials/footer.html, but need to add contacts block.
- Copy
themes/mytheme/layouts/partials/footer.htmltolayouts/partials/footer.html - Make changes
If theme is well-structured, footer split into sub-partials:
layouts/partials/footer/
├── contacts.html
├── nav.html
└── copyright.html
Then only override layouts/partials/footer/contacts.html.
Adding New Page Type
If theme doesn't provide, say, "Team" page:
content/team/
_index.md # Team list
ivan-petrov.md # Team member page
layouts/team/
list.html # List template
single.html # Member template
# content/team/ivan-petrov.md
---
title: "Ivan Petrov"
role: "CTO"
photo: "ivan.jpg"
order: 1
---
Biography and description...
Theme Update
# Git submodule
git submodule update --remote themes/mytheme
# Hugo Modules
hugo mod get -u github.com/theNewDynamic/gohugo-theme-ananke
After update check: overridden templates didn't break due to theme changes. CI pipeline should include hugo --buildFuture --buildDrafts for verification.
Timeframe
Setup ready theme for client content (colors, fonts, menu, params) — 1–3 days. Deep customization with template overrides, adding new content types, custom styles — 3–7 days.







