Why I'm Transitioning to Static Websites
Moving from Wordpress to Hugo
Note: This does not serve as a guide. It is only a reminder in case I forget this again.

What’s a static website? What are advantages of static websites? Please check this blog from Cloudflare.

I’m not in a position to say how one option is correct and the other option is not. This is only a log for myself.

Initial choice: WordPress

The reasons for which I chose to use WordPress in the beginning is simple:

  • Easy to follow tutorials
  • Beginner-friendly editor
  • Not too bad-looking pages
  • Abundant choice of themes/plugins

But after using WordPress for a while, I also realized some of its weaknesses:

  • Performance is much worse than static pages
  • CVEs appear often
  • I don’t need most of the functions a dynamic site provides
  • The editor can be hard to use sometimes

Turning to Hugo

There are a lot of choices for static site generators. I chose Hugo for the following options:

  • Good performance
  • Great plugins/themes/documentation
  • The fact that it uses Markdown for editing

At the same time, I use VSCode and Git for writing and source management, and overall have a wonderful experience.

Although initially I did not have much interest for solutions that require me to use the command line and external editors, after about half an hour of reading the documentation, it turned out to be not too difficult.

I also plan to remake the DN42 pages and other websites to be static where possible, to have a better editing experience.

Common knowledge and commands for Hugo

Source of truth for this part comes from the Hugo Docs

Quickstart

hugo new project quickstart

This command asks hugo to create a folder called quickstart, and generates a project skeleton within that folder. The project skeleton looks like this.

my-project/
├── archetypes/
│   └── default.md
├── assets/
├── content/
├── data/
├── i18n/
├── layouts/
├── static/
├── themes/
└── hugo.toml         <-- project configuration

Depending on your site’s requirements, you might not need some of those folders. After the site is built, the folders public/ and resources/ are also creates as the built files.

Each of the folders contribute to the website’s content or behaviour. For what each folder is used, refer to this docs

Page bundles

Hugo uses a folder structure that bundles images and other resources into Page bundles. Page bundles are content folders that holds index.md or _index.md at their foot. The resources within page bundles are called page resources, and they are only available to the page with which they are bundled.

Content should be organized in a manner that reflects the rendered website. For example, the following structure should work without additional configuration.

.
└── content
    └── about
    |   └── index.md  // <- https://example.org/about/
    ├── posts
    |   ├── firstpost.md   // <- https://example.org/posts/firstpost/
    |   ├── happy
    |   |   └── ness.md  // <- https://example.org/posts/happy/ness/
    |   └── secondpost.md  // <- https://example.org/posts/secondpost/
    └── quote
        ├── first.md       // <- https://example.org/quote/first/
        └── second.md      // <- https://example.org/quote/second/

A more detailed example with page resources could look like this.

content
└── post
    ├── first-post
    │   ├── images
    │   │   ├── a.jpg
    │   │   ├── b.jpg
    │   │   └── c.jpg
    │   ├── index.md (root of page bundle)
    │   ├── latest.html
    │   ├── manual.json
    │   ├── notice.md
    │   ├── office.mp3
    │   ├── pocket.mp4
    │   ├── rating.pdf
    │   └── safety.txt
    └── second-post
        └── index.md (root of page bundle)

Note that the page resources of page first-post are not visible to page second-post.

content/
├── blog/
│   ├── hugo-is-cool/
│   │   ├── images/
│   │   │   ├── funnier-cat.jpg
│   │   │   └── funny-cat.jpg
│   │   ├── cats-info.md
│   │   └── index.md
│   ├── posts/
│   │   ├── post1.md
│   │   └── post2.md
│   ├── 1-landscape.jpg
│   ├── 2-sunset.jpg
│   ├── _index.md
│   ├── content-1.md
│   └── content-2.md
├── 1-logo.png
└── _index.md

This example shows nested page bundles, namely content/_index.md, content/blog/_index.md, and content/blog/hugo-is-cool/index.md. Note that the home page cannot contain other content pages, although other files such as images are allowed.

Hugo only makes sure each content is rendered to the target paths, however the navigation to the contents at each path are up to the website’s author to implement, as well as the theme to decide on its apperance. For example, a blog based theme could list all of its posts at the home page, while a company facing theme probably won’t make each product page visible directly on the root page. Therefore it is important to refer to the theme’s manual and make changes accordingly.

Initializing Git

cd quickstart
git init
git submodule add https://github.com/gohugo-ananke/ananke themes/ananke

Of course, you would use git for source control on your content. Here we can add a theme as a Git submodule, to the path themes/ananke. More about this will be featured on the part, “How to use Git submodules”.

Editing and viewing content

To add a new page to the project’s content, you would use the following command.

hugo new content content/posts/my-first-post.md

Hugo will create the file to the path do designated, from the archetype you chose, which is in this instance posts.

By default the page’s front matter will set the value of draft to true, which will not be published by default.

To start Hugo’s development server, you would run the following command.

hugo server

However, to include draft content, you would run the following command.

hugo server --buildDrafts
hugo server -D

Publishing the project

When you choose to publish your project, Hugo renders all build artifacts to the public directory in the root of your project. This includes the HTML files for every site, along with assets such as images, CSS, and JavaScript. The command to do so is very simple.

hugo

It might be important to remember that Hugo does not clear the public directory before building your project. Existing files are overwritten, but not deleted. This behavior is intentional to prevent the inadvertent removal of files that you may have added to the public directory after the build. Depending on your needs, you may wish to manually clear the contents of the public directory before every build.

Same goes with the resources folder, which is said to By default this cache directory includes CSS and images. Hugo recreates this directory and its content as needed. Therefore in some cases it might be benefitial to remove that folder by hand.

How to use Git submodules

Source of truth to this section should be on the Git handbook

What are submodules?

Sometimes you need to refer to another repository from your own. However, if you simple refer to it by code by pointing to another directory outside of the current repository, you lose the advantages of using Git for version control on that, making updating it when newer upstream sources are available extremely difficult. Therefore, normally it is preferable to hold the code of that repository, within the same Git version controlled environment.

There are a few ways to do that. For example, some programming languages might have builtin package managers that allow importing packages. However, as is the case with Hugo themes, sometimes we do need the raw code at our hands. There is also the problem with simply including the library, which is that it is difficult to customize the library in anyway, or to make sure each time that the library is installed on a target machine.

Git addresses this problem with submodules, which allows the user to keep another Git repository as a subdirectory of a Git repository, while also keeping the commits separate.

Other advantages of using Git submodules also include:

  • Code safety when an external component is changing too fast, or when upcoming changes will break your project.
  • When an external component doesn’t change too often, and you want to track it as a dependency.

Hugo modules

Hugo allows the use of external resources as modules through its builtin management feature.

A module is a packaged combination of components which may contain archetypes, assets, content, data, templates, translation tables, and static files. A module may be a theme, a complete project, or a smaller collection of one or more components.

Source: Hugo docs

To put it simply, Hugo provides a way to manage external resources used by Hugo through the hugo mod command, which allows inclusion of external files through a subdirectory in the project folder, a external path on the system’s mountpoint, or even just a link to a Git repository. Importing, updating, caching, removing and other actions are all controlled by Hugo.

I do believe that this is a great tool to get started when other resources are necessary. However, since that this is a function confined to Hugo, the option to use the submodule function of Git, which could also be used in other projects unrelated to Hugo, could be a better option.

Quirks of Git submodules

Although submodules are represented by just another directory on your filesystem, Git sees it as a submodule and doesn’t track its contents when you’re not in that directory. Instead, it sees the directory as a whole, as a certain commit.

Therefore, when you wish to update contents of the submodule, it might not be as straightforward as editing a simple file, as there is a flow that needs to be followed.

  • Go to the submodule’s directory
  • Edit the submodule
  • Commit changes to the submodule
  • Push the submodule’s changes to remote
  • Now the parent repository sees a new version to the submodule
  • Commit changes to the parent repository, and pushto remote

It is important that the editor does not forget to push changes to the submodule to remote, before the parent repository is pushed. This is because Git only tracks the submodule to a commit, and when the parent repository is pushed without the new version of the submodule being available, other users trying to use the repository will fail to do so.

Common commands to use Git submodules

Again, the source of truth is the Git handbook, linked above. This part will only cover some common commands that I will probably use often.

Adding a submodule

To start to use a submodule, you would use the following command.

git submodule add https://link-to-repo

This will result in the repository being cloned, and the .gitmodules file created.

[submodule "submodule-a"]
    path = submodule-a
    url = https://link-to-repo

Note that if you’re using code from another developer, it might be a good idea to fork that repository first. This is because while using submodules, you probably with to edit the files to a certain point, however using the original developer’s repository directly does not allow your changes to be pushed to remote, at least the changes that only apply to yourself.

Cloning a repository with submodules

When cloning a project with a submodule in it, by default the directories that contain the submodules will be created, but not the contents inside those directories. To also clones the submodules, you need to run these commands.

git submodule init # inits local configuration
git submodule update # fetches data from remote

Another way to do this is to pass --recursive-submodules to the git clone command. This has the advantage of being able to recursively fetch data if any of the submodules in your repository has submodules of themselves.

git clone --recursive-submodules https://link-to-repo

Finally, if you have already cloned the repository but forgot to use --recursive-submodules, you can combine git submodule init and git submodule update with the following one liner.

git submodule update --init
git submodule update --init --recursive # if submodules are recursive

Updating upstream changes from submodule remote

One common way to use submodules is to consume its content, but not make any modifications on your own. Thus, it might be necessary to update the submodule from its upstream from time to time.

To check for new work in a submodule, go into its directory and run these commands.

git fetch
git merge origin/master

If you commit at this point then you will lock the submodule into having the new code when other people update.

An easier way to do this exists, if you do not wish to fetch and merge in the subdirectory by hand.

git submodule update --remote
git submodule update --remote submodule_name # when only updating one submodule

Last modified on 2023-02-12