1. 33
  1. 8

    Hey folks this is my post. Thanks for the comments! I’m not making A Statement, I just am trying to learn a thing and keep notes in public about it. But I will editorialize here; I found the Hugo docs super confusing. There’s a lot of docs but somehow I read them twice and didn’t come away understanding what Hugo actually does. Hoping my notes help someone else in the same boat.

    1. 4

      I had a similar experience to you: I wanted to build a simple static website, I decided to use Hugo, and the official docs didn’t help me get started. I tried to use some existing themes but they were all opaque to me; I struggled to customise them how I wanted.

      I ended up reading Build Websites With Hugo, which takes the same approach as your article: create a custom theme, and build up the layouts etc. from there. That made it a lot easier.

      The book’s available on O’Reilly Online, which I access via my ACM membership but is also often available through your local library. It’s definitely worth a look.

      1. 2

        I had a similar experience. It has documentation, and it’s better than the alternative, but for reasons I’ve always had trouble putting my finger on, it takes a lot of work beyond the docs to understand.

        1. 6

          I’ve had the same trouble. IMO it’s because it looks like comprehensive documentation, but it’s all just reference docs, and poorly organized references at that.

          1. 2

            Yeah, I think there are a couple of intersecting problems:

            1. Hugo has grown enormously over the years. I started using it around 2016, at which point it didn’t support e.g. base templates because Go didn’t support base templates. The layering of new features is good if you’re continuously using it (“hey, cool, I can rewrite X to be much simpler!”) but bad for new users. Which is related to

            2. It uses Go’s templates as its core. (It actually used to support a HAML-like templating language, but that was dropped at some point.) Go templates are idiosyncratic (less politely, weird), and to understand them you have to read https://pkg.go.dev/text/template very carefully. I had done that already when I started using Hugo because I like Go and wanted to use Go as a web server anyway, but the average website theme creator just knows HTML and CSS and doesn’t want to learn anything Go specific.

            3. The documentation is as mentioned, all reference docs. It’s not that bad as a reference, but there are areas where old systems haven’t had their docs updated to explain how they work with new systems (e.g. asset pipelines for pages vs. the site). However, it definitely needs tutorials and cookbooks to actually round it out.

            A different problem from my point of view as a Go developer is that the code base is too gnarly to extract anything from it. I would love to be able to use, e.g., Hugo’s template functions in my Go templates, but god help anyone who wants to figure out how they’re exported. (I think there is some sort of namespace struct that does something??? I really could not follow it in my attempts to figure it out.)

            I’d love to make a new, simpler SSG based on the learnings of Hugo and the development of Go (second system syndrome), but I don’t think it will happen because Hugo already exists and is hard to challenge.

      2. 7

        Count me as another vote for Hugo and custom themes. I’ve been blogging with Hugo and my own theme for the past 5 years. It’s been great.

        1. 2

          Yeah, my website/blog https://michael.stapelberg.ch/ is Hugo based, too, since 2018!

          Hugo indeed is blazingly fast and has a bunch of nice features, including live reload. Definitely the nicest static site generator I currently know of! :)

        2. 3

          I like Hugo very much and use it for some of my websites. It took some hours to get acquainted with, but that was really worth it IMHO. (YMMV.)

          However, I found that many themes has way too much cruft in them. JavaScript for example, which I don’t need at all.

          Therefore I decided to skip themes altogether.

          For styling I chose the formidable water.css stylesheet. Thanks to that choice I didn’t have to write a single line of CSS.

          The markup/templating code I needed I just checked right in to the blog repo itself. It’s like ~30-40 lines in total, I felt no need to bundle that up as a theme in a separate repo.

          Check it out my Hugo playground here: https://github.com/cpach/piper

          Feel free to clone it if you like!

          If anyone is looking for a good tutorial, I can warmly recommend this one: Make a Hugo Blog From Scratch

          1. 3

            Does anyone else find it a bit funny that a post about Hugo themes is hosted on SaaS Wordpress?

            1. 2

              I will say that dogfooding my own Hugo theme has helped me find bugs / edge cases / missing features, and I highly recommend it for anyone developing a theme. I have three websites running my theme and the slightly different use cases have highlighted different problems.

              One problem is that, until I’m happy with my theme’s support for basic things like images and video embeds (no, I’m not satisfied with the out-of-the-box Hugo behavior), it’s difficult to write even self-referential posts where you might want to include screenshots of e.g. results of your theme code in different browsers. (The OP is a simple text post with code blocks that my theme currently handles fine, I’m just pointing out an example of the difficulties with dogfooding a work-in-progress theme.)

            2. 2

              This is really great. I personally blog with Pelican because I’m Python and not Golang driven, but I’ve been wanting to invest the time in rolling my own theme and thus far failing :) This article will help.

              1. 3

                I started with Pelican first because I’m also a Python person. But I decided to switch after it looked like Hugo had more momentum, more themes, etc. Also I realized I wasn’t going to hack the blog generator so did I care about what language it was in?

                The irony is now I’m having to write Go Templates and while it’s not much programming, I do wish it were closer to Python.

                1. 1

                  That’s the thing. Hugo seems neat but there’s ALWAYS gonna be something, right? And IMO having to dive into a completely alien ecosystem can be a TON of fun if you have the time, but I never have the time :)

                  How did you get your posts converted from rst to whatever Hugo usees?

                  1. 4

                    I’m starting with posts in an ancient Perl blog system called Blosxom. The posts are more or less in HTML, only I have some custom tags that work like Hugo shortcodes. I wrote a custom convertor to HTML + TOML header info that Hugo can use.

                    1. 4

                      I’m pretty sure your blog is the oldest and biggest Blosxom install still extant. Are you running it in static mode?

                      I recently started a Blosxom gemlog because through the years I’ve found that running Blosxom statically makes it really easy to set up stuff that’s presented in a chronological order, like twitter feeds or IRC logs. I know modern SSGs are more capable but right now I’m almost perversely invested in continuing to use Blosxom.

                      1. 3

                        Oh no, it’s dynamic. I think the CGI takes longer to run to display one post than Hugo takes to format all 1771 posts. Blosxom does have a nice static mode though and was one of the first to do it.

                        I arrogantly thought I might have the only Blosxom blog left; glad to hear you make good use of it. It’s remarkable to me how 20 years later Blosxom still does more out of the box with generating URLs than modern blog engines. I’m still working out how to get Hugo to emit both per-hierarchical-category and per-month pages.

                      2. 2

                        Hey I remember Blosxom! That was a great package in its time :)

                2. 2

                  Oh, nice! I just launched a new version of my personal site using Hugo, with automatic build/deploy from gitlab CI on my private repo (just because I felt like trying that, haha). One challenge I found was that it was tough to find an extremely-minimal responsive theme that looked good and had an absolute minimum of JS (would actually prefer zero JS, but I didn’t manage to really find anything). I’ve been thinking about creating my own ultra-minimalistic theme, and this article appears to be a great reference point to inform my process!

                  I agree with the author’s comment in the discussions here - I find Hugo’s documentation a bit confusing, and IMO it obscures a lot of functionality with what I consider to be fairly unusual terminology. Like “front matter”, what is that? Is that like, just a block of metadata at the top of the file? Is it an abstract term for “rules and values that may affect the rendering of the respective page”? The term is never actually explicitly defined anywhere in Hugo’s documentation that I could find. It appears to originally be a publishing term, now reused in the context of static site generators. I’ve now built and launched a site and still don’t quite understand the term, though I found docs for other SSGs that seem to describe it. Does that definition apply for Hugo? I guess so? /shrug

                  1. 2

                    Frontmatter is the block of metadata at the top of a content file. AFAIK, the term was first popularized by Jekyll and is now pretty standard across SSGs.

                    1. 2

                      Right, I figured that much out, but only by accident. Here’s the thing: it doesn’t actually say anywhere in the documentation that the “front matter” block goes at the top of the page! I just tried putting the “front matter” block lower down in the page and, lo and behold, the page doesn’t render properly anymore. Fundamental stuff like that should absolutely be explicitly mentioned in the documentation. They nicely explain that it allows you to set some page settings and those settings can be specified in different formats, and… that’s about it. :(

                      (Regardless of all that, thanks for helping clarify!)

                  2. 1

                    I manage a news site with Hugo. You can look at the layout if you’re interested here: https://github.com/spotlightpa/poor-richard

                    1. 1

                      I have been working on creating a Hugo theme from scratch as well, in part using that same tutorial. Sadly, despite my efforts to keep it minimal, it has rapidly increased in complexity and may continue to do so. Still, people interested in minimal themes may find my struggles educational.

                      Here’s my theme’s source repo:

                      https://git.sr.ht/~skyfaller/maxethics-hugo

                      And a test site using that theme:

                      https://www.maximumethics.dev/

                      The repo for the content of that site:

                      https://git.sr.ht/~skyfaller/maximum-ethics-content

                      If I ever finish the theme / get it to a place where I’m happy with it, I will probably write it up and post it to Lobsters.

                      1. 2

                        Hey, I like this! Nice and minimal. Am I seeing correctly there’s no JS used for the theme (other than the counter thing you’ve got)? Will have to take a look at this for inspiration while making my own minimal theme. Cheers!

                        1. 2

                          Yeah, I hate JavaScript. Any JS I include will be optional, the theme should function fine with JS disabled in your browser. For example, I may use JS for video embeds, but leave a hyperlinked thumbnail for noscript users to take them directly to the video. (You would be able to avoid this entirely by not embedding videos.)

                          The counter JS is in the content added by the user (me in this case), not the theme. I actually think visitor stats are generally useless, I just wanted to experiment with more ethical counters I could set up for clients instead of Google Analytics if they insist (not everyone agrees with me). GoatCounter seems good so far.

                      2. 1

                        I understand your frustrations in your other post. Hugo is certainly complicated. Just the previous week, I experimented to create a bare Hugo blog without any theme to get a list of posts on the home page and gave up. Zola was pretty easy to do this with, though a bit early. Thanks for persisting. I will try your approach.

                        1. 2

                          Huh, that seems like a failure of docs. As an experience Hugo user creating a blank homepage with just a list of links to other pages is pretty trivial:

                          <!-- layouts/index.html -->
                          {{ range site.RegularPages }}
                             <a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
                          {{ end }}
                          

                          If docs don’t make it clear how to do this, the docs need to be rewritten.

                          1. 1

                            I finally got to there in my blog post, but it required someone else’s tutorial blog post for me to get there. That little “trivial” thing contains a whole lot of magic the Hugo docs don’t explain well. Like the role layouts/index.html plays in a Hugo site. Or what that range function does. Also you didn’t do this but you really want to define that whole snippet inside a “main” block so the templating works like you’d expect. These are all complicated ideas to figure out if you’ve never used Hugo before and the docs don’t do a great job getting you there.

                            I think it’d be a big help if the “new theme” wizard included boilerplate like yours. It’s the bare minimum you expect from a blog engine.

                            1. 1

                              I think you mistakenly believe there is something special about the name “main”. I believed and did a quick experiment to prove to myself that Hugo just looks for baseof.html and then applies it if the page template (index/list/single.html) has a define directive.

                              Eg. This inherits baseof.html and renders the list of pages:

                              -- layouts/baseof.html --
                              <!DOCTYPE html>
                              <html>
                                  <body>
                                      I am baseof.html
                                      <div>
                                      {{ block "blorp" . }}
                                              Default content.
                                      {{ end }}
                                      </div>
                                  </body>
                              </html>
                              -- layouts/index.html --
                              {{ define "blorp" }}
                                 {{ range site.RegularPages }}
                                    <a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
                                 {{ end }}
                              {{ end }}
                              

                              And this renders “Default content”:

                              -- layouts/baseof.html --
                              <!DOCTYPE html>
                              <html>
                                  <body>
                                      I am baseof.html
                                      <div>
                                      {{ block "blorp" . }}
                                              Default content.
                                      {{ end }}
                                      </div>
                                  </body>
                              </html>
                              -- layouts/index.html --
                              {{ define "zorp" }}
                                I am not shown because baseof.html does not refer to "zorp".
                              {{ end }}
                              

                              And this renders index.html without using baseof.html:

                              -- layouts/baseof.html --
                              <!DOCTYPE html>
                              <html>
                                  <body>
                                      I am baseof.html
                                      <div>
                                      {{ block "blorp" . }}
                                              Default content.
                                      {{ end }}
                                      </div>
                                  </body>
                              </html>
                              -- layouts/index.html --
                              I don't use baseof.html because there is no "define" directive.
                              
                              1. 2

                                The special thing about the block named “main” is the baseof.html that “hugo new theme” generates has boilerplate to include it. I’m sure you can change it.

                                Thanks for explaining more about how the presence of a define directive may be what changes Hugo’s behavior to include baseof.

                        2. 1

                          If you prefer control over the generation process and prefer minimalism might want to take a look at my static site generator https://mkws.sh.

                          1. 1

                            I dunno, I think if you’re not going to use one of the big names, you may as well just DIY instead of using someone else’s. It’s not a big deal to write your own SSG. I wrote one in Python back in 2012. The point is to write it yourself so you get to make all the idiosyncratic assumptions that make sense to you. You know exactly how it works, because you built it, and if it works wrong, you’re comfortable making it do something else. You don’t get that advantage from using someone else’s minimalist code.

                            1. 1

                              I dunno, I think if you’re not going to use one of the big names, you may as well just DIY instead of using someone else’s

                              That’s an option too. mkws is almost DIY.