So you wanted to get started with hugo? Its one of the most used static site generators out there and its pretty powerful. However, that being said the new user experience is lackluster, so I wrote this guide to help make getting up and running with it an easier endeavor. This guide is light on images as I want to focus on the essence of hugo rather than tangential semantics. I assume that you have already have everything installed and are smart enough to understand how to get hugo installed and running.
First navigate to where you want to put the site files. Use the cli to create a new site.
sh$ hugo new site .
Congratulations! Your new Hugo site was created in /home/theorytoe/projects/hugo_tut2.
Just a few more steps...
1. Change the current directory to /home/theorytoe/projects/hugo_tut2.
2. Create or install a theme:
- Create a new theme with the command "hugo new theme <THEMENAME>"
- Or, install a theme from https://themes.gohugo.io/
3. Edit hugo.toml, setting the "theme" property to the theme name.
4. Create new content with the command "hugo new content <SECTIONNAME>/<FILENAME>.<FORMAT>".
5. Start the embedded web server with the command "hugo server --buildDrafts".
See documentation at https://gohugo.io/.
You get some output (that we will ignore) and you’ll notice that your working directory is now populated with some items. I’ll be focusing on the following and ignoring the rest.
archtypes
: Defines the default template for new content made by hugo new
.content
: Markdown files containing the actual content of the website.layouts
: This contains the html skeletons in which we will put our content
into.static
: Static resources, like css and images.hugo.toml
/config.toml
: The configuration file for the site.This is the structure of a basic website, however, we are missing a couple
things in order to actually see any content. If we run hugo
with no args, we
will see the new public
directory with all of the built content in it. You’ll
notice this directory is empty. Lets fix this!
Hugo content is interlaced into layouts, these define how the content is
placed into the rest of the document. In hugo, we have 3 primary types of
layouts for markdown/html documents. The default layouts that apply to every
page are in layouts/_default
.
Generally, most hugo sites will write the main document skeleton in
layouts/_default/baseof.html
Then have blocks that are defined by
single.html
and list.html
respectively. Lets go ahead and define these
layouts.
layouts/_default/baseof.html
:
<!doctype HTML>
<html>
<head>
<title>{{ .Site.Title }}</title>
</head>
<body>
{{ block "main" . }}
<p>No content to be found!</p>
{{ end }}
</body>
</html>
layouts/_default/single.html
:
{{ define "main" }}
<article>
{{ .Content }}
</article>
{{ end }}
layouts/_default/list.html
:
{{ define "main" }}
<ul>
{{ range .Pages }}
<li><a href='{{ .RelPermalink }}'>{{ .Title }}</a></li>
{{ end }}
</ul>
{{ .Content }}
{{ end }}
Now if you run hugo serve
in your shell and open the link it gives you in your
browser, you will see a page! Its rather empty however, so lets change that. By
default, hugo will define a default archtype for a post. I generally dont like
the TOML front matter, so we will be changing it from toml to yaml (which has
better editor support for being integrated into markdown).
--- archetypes/default.md 2024-10-29 21:20:08.665684146 -0700
+++ archetypes/default.md.~1~ 2024-10-31 13:19:34.784986313 -0700
@@ -1,5 +1,5 @@
-+++
-title = '{{ replace .File.ContentBaseName "-" " " | title }}'
-date = {{ .Date }}
-draft = true
-+++
+---
+title: "{{ replace .File.ContentBaseName "-" " " | title }}"
+date: {{ .Date }}
+draft: true
+---
Now if you run hugo new page.md
, hugo will create a new file in
content/page.md
with the default archetype that we just modified. Open the
file in your editor and add some text to it. Make sure to remove the draft
option in the metadata or you wont see the page!
--- /dev/null 2024-10-31 12:52:51.231645990 -0700
+++ content/page.md 2024-10-31 13:22:42.938320466 -0700
@@ -0,0 +1,10 @@
+---
+title: "Page"
+date: 2024-10-31T13:22:12-07:00
+---
+
+
+# Webpage
+
+This is a sample webpage.
When you open the browser, you’ll just see a link to the page that you just made. When you click the link, you will see what you wrote.
Now that we have a page, lets create a menu. Start by adding this into
layouts/_default/baseof.html
.
--- baseof.html~1~ 2024-11-16 13:33:57.609448357 -0700
+++ baseof.html 2024-11-16 13:33:12.326114708 -0700
@@ -3,6 +3,13 @@
<head>
<title>{{ .Site.Title }}</title>
</head>
+<header><nav>
+{{ range site.Menus.main }}
+ <a href="{{ .URL }}">
+ {{ .Name }}
+ </a>
+{{ end }}
+</nav></header>
<body>
{{ block "main" . }}
<p>No content to be found!</p>
With this, you can either include a page in a menu by adding the following to any page front matter:
menus: main
Or by adding the menu to the site configuration:
[menus]
[[menus.main]]
name = 'Home'
pageRef = '/'
weight = 10
[[menus.main]]
name = 'Articles'
pageRef = '/articles'
weight = 20
Both contribute to the menu, but the method of editing the site configuration gives you a bit more control over the menu as opposed to just including it in the page.
Now we have a page with a page and a menu, lets add to that ‘Articles’ section we added in the menu.
content/articles/article1.md
:
--- /dev/null 2024-11-16 12:50:29.706096829 -0700
+++ content/articles/article1.md 2024-11-16 13:55:24.456124003 -0700
@@ -0,0 +1,11 @@
+---
+title: "Article1"
+date: 2024-11-16T13:53:04-07:00
+---
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
+incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
+nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
+Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
+fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
+culpa qui officia deserunt mollit anim id est laborum.
content/articles/article2.md
:
--- /dev/null 2024-11-16 12:50:29.706096829 -0700
+++ content/articles/article2.md 2024-11-16 13:55:25.419457343 -0700
@@ -0,0 +1,39 @@
+---
+title: "Article2"
+date: 2024-11-16T13:53:11-07:00
+---
+
+Lorem ipsum odor amet, consectetuer adipiscing elit. Fringilla aliquet nisl
+facilisi velit mus accumsan sodales. Quam orci convallis primis fermentum
+feugiat nam amet. Tortor et dapibus maximus potenti dictum erat. Tortor velit
+primis turpis laoreet quis habitant luctus hendrerit. Eget leo inceptos
+pharetra augue porta eu egestas feugiat. Ac ut vel magna rutrum bibendum urna
+amet sociosqu.
+
+Mollis mi ex fermentum quisque semper elementum? Eros sem in metus condimentum
+lacus posuere consequat nunc montes? Dictum facilisi tortor penatibus auctor
+molestie fringilla vestibulum. Morbi suscipit donec imperdiet ac primis mus
+rhoncus neque? Risus cursus congue facilisi adipiscing curae consectetur?
+Aliquam nec habitant lobortis nascetur tempus consequat orci morbi. Tincidunt
+luctus nec sollicitudin erat aliquam nisi aliquet cubilia.
+
+Scelerisque ullamcorper fermentum faucibus porta, ligula eget. Lacinia
+elementum a scelerisque sagittis, risus nunc neque odio. Eros dolor tellus quis
+vitae pretium bibendum? Molestie semper gravida nunc habitant porta consequat
+fusce vehicula luctus. Senectus nostra placerat fringilla; fames risus
+tristique sagittis placerat. Consequat aliquam non curae in habitant egestas
+duis eros.
Now if you go to /articles on the site, you’ll get a listing of the documents
within that section. However, we will probably want to add some text to this
landing page, so lets define an index document. In the root of any section, if
you have a file named _index.md
, its content will be added to the list
template, so long as you added the {{ .Content }}
directive to the template.
content/articles/_index.md
:
--- /dev/null 2024-11-16 12:50:29.706096829 -0700
+++ content/articles/_index.md 2024-11-16 14:03:57.712794250 -0700
@@ -0,0 +1,6 @@
+---
+title: "Articles"
+date: 2024-11-16T14:03:39-07:00
+---
+
+This is a collection of my articles! Enjoy reading them!
Now we can see the text, but the list of articles is after the list. Ideally we
want the text to follow the list. As well, I want to make the articles have the
date they were created put before the link. Lets do that by making a copy of the
default list template that we made, and make it specifically for the articles
section.
sh$ mkdir layouts/articles
sh$ cp layouts/_default/list.html layouts/articles/list.html
Then lets change the template:
--- layouts/_default/list.html 2024-10-31 13:28:33.218321992 -0700
+++ layouts/articles/list.html 2024-11-16 14:09:25.862796540 -0700
@@ -1,9 +1,9 @@
{{ define "main" }}
<h1>List of {{ .Title }}</h1>
+{{ .Content }}
<ul>
{{ range .Pages }}
- <li><a href='{{ .RelPermalink }}'>{{ .Title }}</a></li>
+<li>{{ .Date.Format "2006-01-02" }} <a href='{{ .RelPermalink }}'>{{ .Title }}</a></li>
{{ end }}
</ul>
-{{ .Content }}
{{ end }}
With this, the body text comes before the list of articles, and we additionally have a timestamps before the link to the article.
RSS is a spec that defines a xml structure that represents a feed of your content. People often subscribe to feeds with feed readers. Sort of how you subscribe to a magazine. (does anybody read magazines anymore?) Hugo has some defaults in place, but it has some things to be desired, so we will edit the default template to display the entire content of an article, rather than just a summary.
layouts/_deafult/rss.xml
:
--- /dev/null 2024-11-16 22:01:45.086566412 -0700
+++ layouts/_default/rss.xml 2024-11-16 23:12:52.529918361 -0700
@@ -0,0 +1,59 @@
+{{- $authorEmail := "" }}
+{{- with site.Params.author }}
+ {{- if reflect.IsMap . }}
+ {{- with .email }}
+ {{- $authorEmail = . }}
+ {{- end }}
+ {{- end }}
+{{- end }}
+
+{{- $authorName := "" }}
+{{- with site.Params.author }}
+ {{- if reflect.IsMap . }}
+ {{- with .name }}
+ {{- $authorName = . }}
+ {{- end }}
+ {{- else }}
+ {{- $authorName = . }}
+ {{- end }}
+{{- end }}
+
+{{- $pctx := . }}
+{{- if .IsHome }}{{ $pctx = .Site }}{{ end }}
+{{- $pages := slice }}
+{{- if or $.IsHome $.IsSection }}
+{{- $pages = $pctx.RegularPages }}
+{{- else }}
+{{- $pages = $pctx.Pages }}
+{{- end }}
+{{- $limit := .Site.Config.Services.RSS.Limit }}
+{{- if ge $limit 1 }}
+{{- $pages = $pages | first $limit }}
+{{- end }}
+{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
+<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
+ <channel>
+ <title>{{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{ . }} on {{ end }}{{ .Site.Title }}{{ end }}</title>
+ <link>{{ .Permalink }}</link>
+ <description>Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{ . }} {{ end }}{{ end }}on {{ .Site.Title }}</description>
+ <generator>Hugo</generator>
+ <language>{{ site.Language.LanguageCode }}</language>{{ with $authorEmail }}
+ <managingEditor>{{.}}{{ with $authorName }} ({{ . }}){{ end }}</managingEditor>{{ end }}{{ with $authorEmail }}
+ <webMaster>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</webMaster>{{ end }}{{ with .Site.Copyright }}
+ <copyright>{{ . }}</copyright>{{ end }}{{ if not .Date.IsZero }}
+ <lastBuildDate>{{ (index $pages.ByLastmod.Reverse 0).Lastmod.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
+ {{- with .OutputFormats.Get "RSS" }}
+ {{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
+ {{- end }}
+ {{- range $pages }}
+ <item>
+ <title>{{ .Title }}</title>
+ <link>{{ .Permalink }}</link>
+ <pubDate>{{ .PublishDate.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
+ {{- with $authorEmail }}<author>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</author>{{ end }}
+ <guid>{{ .Permalink }}</guid>
+ <description>{{ .Content | transform.XMLEscape | safeHTML }}</description>
+ </item>
+ {{- end }}
+ </channel>
+</rss>
With this, you can access any feed via the index.xml
page.
This marks the end of my short guide on getting up and running with hugo, its short, but it covers 90% of the stuff that isnt directly documented on how to use. This is 90% of the information that you really need to write cool websites with hugo. This guide was purposely short because there is a lot I could talk about, but there are generally better places to find that information.
I would highly recommend the following sites for extra related documentation/resources: