This is the very first blog post on my blog. I think it is symbolic to write about the blog itself. More precisely, about its tech stack.

I wanted to create for myself very fast blog with little-to-no JavaScript, only static HTML and little CSS. Because it’s static, it doesn’t need any fancy servers and so it can be hosted for free on Netlify or GitHub pages (and probably others). Because it doesn’t have any fancy JavaScript, it is fast on any device and for all visitors regardless of their Internet connection speed.

Hosting

For hosting I chose Netlify as I have had some experience with it already. If you don’t know, Netlify/GitHub pages are great options to host any website for free (you only pay for second-level domain name if you need it). Source code of the blog is hosted on my GitHub (this one is public but it can be private for free as well if you don’t feel like sharing source code of your website).

Static site generator

With the described setup, you can simply start writing your blog in pure HTML with some CSS styles to give it nice looks, but there is simpler way. You can write your content in Markdown (or rich-text editor as I describe below) and have it converted to HTML/CSS site (SEO included) at build time. This conversion happens automatically in Netlify whenever you push to main branch of your GitHub repository. Netlify provides enough build minutes for free to rebuild your blog as often as you manage to write content.

There are many static site generators that convert content written in some simple format (usually Markdown) to static HTML website (or sometimes JavaScript single-page app). I chose Hugo which provides lots of functionality out of the box and its builds are blazingly fast.

Note that in Hugo, you simply create posts as separate Markdown pages. Each page can have some properties (e.g., title, date of publication, authors, cover image, etc.) in so-called front matter which is nothing more than YAML/TOML config file embedded at the beginning of Markdown file.

Theme

Static site generator of course needs to put your raw content into some nicely-looking webpage. Hugo has many themes, most of them are tailored for blogs (as is Hugo itself and probably most of the other static site generators, as well). However, I have also created photo gallery in Hugo (portfolio of artist/photographer, actually)—I will write about it soon.

For my blog, I use the beautiful PaperMod theme which exactly fits my needs, because it is

  • simple and fast (consists of just HTML + small CSS, has no unnecessary JavaScript, fonts, icons that would load for every visitor of my blog),
  • SEO optimized (e.g., generates structured data),
  • with light and dark mode (some developers love it, some hate it, but everyone should have a choice).

Getting started

To create a blog similar to mine, you can either fork my repository, or follow these steps:

  1. Install Hugo.

  2. Create new Hugo site (see Hugo Quick Start guide). In the following commands, you can replace blog with any name you want.

    hugo new site blog
    
  3. Add the site to Git (cd blog, git init, …).

  4. Add PaperMod theme (see PaperMod Installation guide):

    git submodule add https://github.com/adityatelange/hugo-PaperMod.git themes/hugo-PaperMod
    
  5. Configure your Hugo website and PaperMod theme—add config.yml file. You can start with something like this:

    baseURL: "https://janjones.me/"
    title: "Jan Jones"
    theme: "hugo-PaperMod"
    
  6. Run local development server:

    hugo server -D
    
  7. Now you can see your website at http://localhost:1313.

  8. Configure Netlify deployment. Netlify looks for netlify.toml file in root of your repository. This is a good starting point for Hugo:

    [build]
      publish = "public"
      command = "hugo --gc --minify"
    
    [build.environment]
      HUGO_VERSION = "0.79.0"
      HUGO_ENV = "production"
      HUGO_ENABLEGITINFO = "true"
    
    [context.deploy-preview]
      command = "hugo --gc --minify --buildFuture -b $DEPLOY_PRIME_URL"
    
    [context.branch-deploy]
      command = "hugo --gc --minify -b $DEPLOY_PRIME_URL"
    

    I have also included these lines to ensure RSS feed file has proper Content-Type:

    [[headers]]
      for = "index.xml"
      [headers.values]
        Content-Type = "application/rss+xml"
    

    And these to redirect from third-level Netlify domain to my own second-level domain:

    [[redirects]]
      from = "https://janjones.netlify.app/*"
      to = "https://janjones.me/:splat"
      status = 301
      force = true
    
  9. By default you get random third-level domain from Netlify (e.g., something-random.netlify.app) where your app is available as soon as you push it to main and it gets built on Netlify (which is usually very fast with Hugo). You can change it to something more appropriate in your site’s settings, although it will still contain netlify.app suffix (so you get something like my-cool-blog.netlify.app). However, you can also buy your own second-level domain (e.g., my-cool-blog.com). I recommend doing that directly through Netlify, it is the easiest way and you also get free SSL certificate (for HTTPS) immediately setup. Go to your Netlify account, select your site, click on “Domain settings” > “Add custom domain” and Netlify will guide you through the purchase (or transferring existing domain).

  10. You can add new post using Hugo command:

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

    Alternatively, you can create the Markdown file yourself, but don’t forget to add front matter with useful properties like title and publication date.

Customizations

I have already many ideas how to improve the theme to make my blog just perfect. I will write more posts about these improvements as I make them; starting now with small changes I already did.

Disabling Highlight.js

My blog obviously contains code snippets. To be readable they need to be highlighted properly. Hugo supports static code-highlighting (static means that code fragments are transformed to HTML tags with proper colors at build time). However, PaperMod theme uses Highlight.js library which does the same thing at runtime in user’s browser (it was probably added before Hugo added support for code highlighting and there is already a PR to remove it).

Highlight.js is enabled in PaperMod in layout/partials/footer.html file. To customize templates in Hugo, you can override these layout files. So, I have created new layout/partials/footer.html file in my repository with the Highlight.js snippet removed.

(I know this is not ideal because it leads to code duplication and you have to manually update the copied layout file whenever you update the theme and the original file changes to ensure consistency. However, this is the only way I know these things can be done in Hugo unless the theme itself provides configuration variables or more fine-grained template files to customize everything. The very popular Hugo template wowchemy, which I have also used previously, has very worked-out customization but I still had to sometimes copy whole layout files to modify just fraction of them.)

Next I have added RSS link to footer and replaced “powered by Hugo” text with link to custom stack page where I can provide more details about the software that powers my blog and it doesn’t clutter every page.

Bonus: Headless CMS

Maybe you are creating a blog for someone who doesn’t know Markdown or you just want to write your blog posts in web browser without the need to open your Markdown editor and terminal for Git (as I think might be my case sometimes). And you still want to host your blog for free on GitHub+Netlify or similar combo. That is where headless CMS comes to the rescue.

I will describe Netlify CMS (it was a natural choice for me since I host the website on Netlify). It allows you to edit your pages in user-friendly interface with rich text WYSIWYG editor, image selectors, etc.

Netlify CMS screenshot

To install Netlify CMS, you just add config.yml and index.html files to static/admin folder, enable Netlify Identity and are good to go. The whole process is described for example in the official Netlify docs.

Netlify CMS is very generic and can work with many static site generators. How the editor emits files that your static site generator understands is controlled by config.yml file. For very popular themes like wowchemy, there is this configuration available with prefilled decent defaults, so you can just start using it. You can use this or some other similar config for most Hugo themes although you might need to slightly modify some properties here and there since every theme might use different ones (although the most important ones like Title and Body are always the same).

I will describe my config.yml file for PaperMod theme I created for this blog. It is quite long (because it is repetitive) so I won’t include it here, but you can find the version I will be talking about on my GitHub.

I have enabled editorial_workflow mode which means that whenever I create new blog post, a PR is opened, can be reviewed and “accepted” in separate stages (which can be done through Netlify CMS UI, as well). Only when it is accepted, it gets merged and the blog post is publicly visible. I do this because I like to have my posts reviewed by another person before I publish them and GitHub PR reviews are great for that. This means I don’t use standard Hugo’s drafts, but if you don’t enable editorial_workflow mode, you should definitely enable drafts, so that you can save your in-progress blog posts into your repository without making them visible to everyone until they are ready.

I have also disabled instant previews because they don’t work very well until custom CSS is provided to Netlify CMS (I plan to do that in the future).

I have made slug (title of the post that is visible in the URL of the page) customizable, but you definitely don’t need to do that—Netlify CMS will automatically convert your titles to URL-safe strings.

And finally, I have used a preview feature of Netlify CMS to ensure images (including cover image of every post) are uploaded alongside the blog post’s Markdown instead of global media folder. There is no particular reason to not use the global folder, I just wanted to have posts and their images more self-contained.

Conclusion

I have shown you how I created my blog (hosted for free on Netlify and GitHub). It consists of simple (yet SEO-friendly) static HTML and CSS files (which is everything a fast blog needs). Once configured, it can be even managed by non-programmer thanks to Netlify CMS.

As I continue adding more functionality to the blog (search, comments, PWA, LaTeX math, etc.), I plan to write more detailed blog posts about each addition.

UPDATE: I have added Cactus.chat-based comments recently (see below). I haven’t written any post about it yet, but it is very simple to integrate.

Let me know what you think and feel free to ask questions about this post in comments below.