Skip to main content

Part 1: Building a blog with GitHub - Configuring Hugo

·4 mins

A woman writing a blog post about how to create a blog

It’s been a long time since I had a blog. In an attempt to start again to write and keep interesting things as my memories, something that could be useful for both myself and others bumping into it, I looked for a cheap but effective solution. Here is the one I’m currently using.

Stack #

TopicChoiceWhy
Static site generatorHugoFastest builds, 400+ themes, Go binary
ThemeCongoFeature-rich, Tailwind-based, active maintenance, live demos at jpanther.github.io/congo
CMSSveltia CMSModern Decap CMS drop-in; GitHub Access Token auth; works on GitHub Pages with zero backend
AuthGitHub PATPersonal Access Token stored in browser — no proxy, no Cloudflare, no OAuth app needed
CommentsGiscusGitHub Discussions-backed, no DB, GitHub OAuth for visitors

Set up #

Prerequisites #

Start by installing Go, gh and Hugo. The following command will use Brew since I’m using a Mac:

brew install hugo go gh

hugo version   # must show hugo v0.x.x+extended
go version     # needed for Hugo Modules (Congo install method)

gh (the GitHub CLI) is certainly optional, but it’s pretty good and fast to operate with instead of searching for the things you need in GitHub settings.

1. Repository & Hugo Scaffold #

Let’s create and clone a new GitHub repo and then create the basic blog structure in the directory:

gh repo create chrisvoo/blog --public --clone
cd blog

# Scaffold Hugo site (--force because dir has .git)
hugo new site . --force

2. Congo Theme via Hugo Modules #

Hugo Modules is the preferred Congo install method (easier upgrades than submodules). The following snippets perfectly reflect this website’s configuration, so it already acts as a demo.

  • Initialize the module:
hugo mod init github.com/chrisvoo/blog
  • Create config/_default/module.toml
[[imports]]
path = "github.com/jpanther/congo/v2"

Then pull it:

hugo mod get -u

Congo uses a split config layout under config/_default/:

config/
└── _default/
    ├── hugo.toml           ← site identity + baseURL
    ├── params.toml         ← theme features
    ├── languages.en.toml   ← title, tagline, menu labels
    ├── menus.en.toml       ← nav items
    ├── markup.toml         ← code highlight settings
    └── taxonomies.toml     ← tags, categories, series

Let’s modify the configuration (config/_default/hugo.toml) for base URL, locale, search bar and tags:

baseURL = "https://chrisvoo.github.io/"
locale = "en"
defaultContentLanguage = "en"
enableRobotsTXT = true

[outputs]
  home = ["HTML", "RSS", "JSON"]   # JSON required for Fuse.js search index

[taxonomies]
tag = "tags"
category = "categories"
series = "series"

[markup]
  [markup.highlight]
    noClasses = false   # emit CSS classes (.chroma .k etc.) instead of inline styles
    style = "monokai"   # Chroma style — controls token colours in the generated CSS

Now the params (config/_default/params.toml) for defining the peculiarities of header, footer and articles:

colorScheme = "congo"         # built-in palette; others: avocado, cherry, fire, ocean, slate
defaultAppearance = "dark"    # auto | light | dark
autoSwitchAppearance = true

enableSearch = true
enableCodeCopy = true
enableImageLazyLoading = true
enableImageWebp = true

showReadingTime = true
showBreadcrumbs = true
showTableOfContents = true
showAuthor = true
showDate = true
showWordCount = false

[header]
  showTitle = true
  layout = "hybrid"

[footer]
  showAppearanceSwitcher = true
  showScrollToTop = true

[article]
  showDate = true
  showTaxonomies = true
  showComments = true
  showRelatedContent = true
  sharingLinks = ["linkedin", "email", "telegram", "reddit", "facebook", "mastodon"]

[list]
  showSummary = true
  showBreadcrumbs = true
  showTaxonomies = true
  groupByYear = true

[homepage]
  layout = "page"
  showRecent = true
  recentLimit = 5

[sitemap]
  excludedKinds = []

[author]
  name = "Christian Castelli"

Something about the language and the website’s title (config/_default/languages.en.toml):

title = "The Castles"
label = "English"

[params]
  description = "Thoughts on software, tech, and whatever."

Finally the menu items. The search icon in the nav requires a menu entry with action = "search". Without this, the search bar never renders even if enableSearch = true.

[[main]]
  name = "Blog"
  pageRef = "posts"
  weight = 10

[[main]]
  name = "GitHub"
  url = "https://github.com/chrisvoo"
  weight = 30
  [main.params]
    icon = "github"
    showName = false
    target = "_blank"

[[main]]
  identifier = "search"
  weight = 99
  [main.params]
    action = "search"
    icon = "search"

Putting the code below in content/_index.md prevents Congo’s page homepage layout from rendering a duplicate <h1> with the site title (which also appears in the header logo). An empty frontmatter file suppresses the title in the page body while keeping the recent-posts list.

---
draft: false
---

The following blog posts will explain the remaining parts to be described. 🤘