VS Code & Hugo: The MoonBooth guide

In this article, I’ll show you all the VS Code tips and tricks I use to save time working with Hugo sites each month.

Ron Erdos
Updated November 20, 2022
Tested with Hugo version 0.106.0

Section 1: Why VS Code for Hugo?

VS Code is a brilliant way to run your Hugo blog

VS Code—short for Visual Studio Code—is probably the best free code editor out there, and a fantastic one-stop shop for running your Hugo blog.

In this article, I’ll show you all the VS Code tips and tricks I use to save many hours each month.

Let’s dive in!

VS Code workflows for Hugo

So, what can you do with your Hugo blog and VS Code?

Well, you can run entire Hugo workflows.

I use it in two main ways:

  1. Content creation
  2. Web design

Let’s take a look at both of these.

1. Content creation workflow

With VS Code and Hugo, you can:

  • Write blog posts, including in draft mode
  • Preview changes in realtime in your browser (Hugo has auto-refresh built-in)
  • Push new content and/or updates live

And all of this with the magic of an integrated git workflow for version control.

2. Web design workflow

Similarly, with VS Code and Hugo, you can:

  • Try out design changes
  • Preview the changes in realtime in your browser (Hugo has auto-refresh built-in)
  • Push the changes live when you’re happy

Here again, we also get the joy of an integrated git workflow.

Let’s now dive in to the details of how to implement this. If you’re already familiar with VS Code, feel free to skip ahead.

Section 2: Setup

How to download VS Code

If you don’t already have VS Code, you can download it here. It’s free, and available on Windows, Mac, and Linux (Ubuntu, Debian, Red Hat, Fedora, SUSE).

What is a VS Code “workspace”?

VS Code uses the concept of “workspaces”, which is basically a way to organise your various projects inside VS Code.

I have a workspace for this site (MoonBooth), another for my Julia School website, a third for the SEO scripts I write for my work, and so forth.

Why should you set up a VS Code workspace for each of your Hugo blogs?

We’ve all been there—you’re working on one project when suddenly you need to switch to another one.

VS Code makes switching between workspaces a breeze.

Say you’re working in workspace A, but you want to switch to workspace B. There’s no need to close the files from workspace A—simply leave them all open, and just close the entire workspace.

When you finish with workspace B and return to workspace A:

  • all the files that were open when you closed workspace A will be open once more, and in the same order
  • each file will be at the same scroll depth as before
  • the cursor in each file (if any) will be in the same position as before
  • the way you’ve arranged your workspace—such as split views for side-by-side editing—will be restored

How to have multiple VS Code workspaces open simultaneously

Want more than one workspace open at once?

No problem—you’ll need a free VS Code extension, though.

I use vscode-workspace-switcher, which puts a “W” icon in the Activity Bar (which is the thin vertical strip of icons that runs down the left-hand side of the VS Code screen).

Clicking on the “W” icon brings up an alphabetically-sorted list of all my workspaces. I can then open a new workspace in either a new VS Code window or in the same window.

I use a Mac, so if I open the new workspace in a new window, and make them both fullscreen, I can switch between them using Mission Control. This is a great experience, in my opinion.

If you like to be organised, I highly recommend creating a workspace in VS Code for each of your Hugo sites. Read on for how to do that.

How to set up a VS Code workspace

Here’s how to set up a workspace in VS Code (for your Hugo blog or another project).

Step 1: Open the folder containing your Hugo blog

In VS Code, open the folder containing your Hugo blog.

(If you don’t have a Hugo blog already, first create an empty folder in the Mac’s Finder, Windows Explorer or your Linux filesystem—I’ll show you how to add the Hugo files and themes later.)

Step 2: Decide whether to trust this folder

If this is your first time opening this folder in VS Code, you’ll be asked if you “trust the authors of the files in this folder”.

This is because VS Code “provides features that may automatically execute files in this folder”.

You can choose to “trust the folder and enable all features”, or alternatively, if you don’t trust the authors, you can select the option to “browse folder in restricted mode”.

For Hugo blogs I choose to trust the folder, but of course, it’s your machine and your choice. If in doubt, as the VS Code documentation says, “leave a folder in Restricted Mode. You can always enable trust later”.

Whatever you decide, VS Code will remember your choice each time you open that folder, although as touched on above, you can toggle the trust state at any time (Command Palette Command+Shift+P on Mac or Ctrl+Shift+P on Windows/Linux> Manage Workspace Trust).

Step 3. Save your workspace

You can save your VS Code workspace (File > Save Workspace As…) when you’re ready, although I do this straight away when setting up a new project.

Your workspace will be saved as a file, with the extension .code-workspace.

I usually name my workspace files the same as the project.

For example, my workspace file for MoonBooth is named moonbooth.code-workspace.

I commit the workspace file to my repo since I’m the only one who edits the site.

However, if you’re working with others, you might choose to have your repo ignore all VS Code workspace files—as different people might like different VS Code settings.

(You can do this by adding .code-workspace on its own line in your .gitignore file.)

Alternatively, you might choose to store your VS Code workspace file outside your repo.

If the name of your folder has spaces in it, VS Code’s suggested filename for your workspace will also contain spaces. This is something that, out of habit as an SEO—I always try to avoid.

Without going into details here, suffice to say that I always change spaces in filenames to dashes. (Of course Google isn’t going to see my VS Code workspace file, but as I say, I do this out of habit because spaces in image or HTML filenames aren’t good for SEO.)

How to customise your VS Code workspace

Everything in this section is optional, but it’s also where a lot of the “joy of VS Code” is to be found.

Here are some of my favourite VS Code workspace customisations.

Customisation #1: Colour-coded workspaces

Although I use dark mode for all my VS Code workspaces, I like the status bar at the bottom and document tabs at the top to use the same colour as the project it powers.

So for example, the VS Code workspace for this site—MoonBooth—uses its website colour of “Ford Moonmist Yellow” (#fad73d). My VS Code workspace for Julia School uses the exact same green (#8CF67B) as its website.

This isn’t just an aesthetic thing; having different colours for different workspaces means that:

  • I am unlikely to start typing in the wrong workspace—this used to happen a fair bit before I colour-coded my workspaces.
  • When using the Mac’s Mission Control to select a workspace, I can find the right one more quickly.

Here’s the entirety of my moonbooth.code-workspace VS Code workspace file for MoonBooth, complete with theme colours:

{
	"folders": [
		{
			"path": "."
		}
	],
	"settings": {
		"workbench.colorCustomizations": {
			"titleBar.activeBackground": "#202020",
			"titleBar.activeForeground": "#fad73d",
			"titleBar.border": "#fad73d",
			"tab.activeBackground": "#fad73d",
			"tab.activeForeground": "#202020",
			"tab.unfocusedActiveForeground": "#202020",
			"tab.unfocusedActiveBackground": "#fad73d99",
			"tab.activeBorder": "#fad73d",
			"statusBar.border": "#fad73d",
			"statusBar.background": "#fad73d",
			"statusBar.foreground": "#202020",
			"workspaceInStatusBar.text": "#202020"
		}
	}
}

Customisation #2: Workspace name in status bar

Aside from having my custom colours to remind me which workspace I’m in, I like to have the name of the workspace in the VS Code status bar. I achieve this with an extension with the outrageous and completely illogical 😂 name of Workspace Name in Status Bar. It does the trick.

Customisation #3: Word count in status bar

Rather than stop to look up the word count, it’s a lot easier to just glance down at the status bar, but a “word count in status bar” feature doesn’t ship with VS Code out of the box. So, I use an extension called Word Count to achieve this trick.

Customisation #4: Clock in status bar

When you’re working in full screen on Mac, the system menu bar disappears, and with it, the system clock. I like to have a clock always visible, so I use an extension named Clock in status bar. Super useful, and it takes up hardly any room in the status bar.

Section 3: Previewing your changes locally with Hugo server

You can preview your Hugo site—including any draft posts—in your browser of choice: Chrome, Brave, Edge and so on. Any changes you save will appear in your browser in real time—there’s no need to refresh the browser manually.

You don’t even need a standalone terminal to fire up Hugo’s server; VS Code has an inbuilt terminal.

How to preview your Hugo site locally using Hugo server

To get a local version of your Hugo site up and running, you just enter the command hugo serve or hugo server into the terminal inside your VS Code workspace. (You can get a new VS Code terminal with Terminal > New Terminal.)

You can also set up your machine so that you can type a shorter command into the terminal to get Hugo’s server up and running. The custom command I use is hs, and the way to do this is with something called “aliases”. More on this below.

Setting up aliases on Mac to save time when running Hugo server

1. Open Terminal

Open Terminal, or a CLI of your choice (iTerm, VS Code etc). I’ll refer to the Mac’s native Terminal app to keep things simple.

2. Open a special file on your Mac where aliases go

Aliases go in a certain file on the Mac, and that file is ~/.bashrc.

The filename can look quite alien for first-time users, but this whole process is actually quite straightforward once you get the hang of it. I promise!

If you have VS Code, the easiest way to open this file is by entering this command into Terminal:

code ~/.bashrc

That will open the ~/.bashrc file you need in VS Code.

If you don’t have VS Code, you could enter this command instead:

open -a TextEdit ~/.bashrc

This will open the ~/.bashrc file you need in TextEdit, a simple text editor that comes pre-installed on your Mac.

3. Add your alias to the file

Now you’ll need to enter a line of text and save the ~/.bashrc file. Here’s what I use—you can copy this verbatim if you like:

alias hs="hugo server --buildFuture --minify --buildDrafts"

Let’s walk through this:

alias hs= Here we declare the name of our new alias to be hs

" We open the quote marks on the right side of the expression

hugo server This is the base command to get Hugo’s server up and running. All the “flags” (the items preceded by the double dashes) that follow are optional.

--buildFuture This is a flag that tells Hugo to display future-dated posts. I choose to have this enabled in local server mode, but you might not want this.

--minify Another optional flag—this one minifies the content.

While this isn’t necessary for local previews, it will hopefully show me any minification errors (e.g. with SVGs) that I might have unwittingly introduced.

--buildDrafts This is the final flag I use when previewing Hugo content locally. This flag has Hugo display any draft posts along with published ones. You may not want this setting—totally fine!

I use this flag because I set all my draft posts to “draft” status—by setting draft: true in their YAML front matter—so I don’t accidentally publish them. I really only use one git branch for my own sites, so this is my safeguard against accidentally publishing a draft.

As you might have concluded, my Hugo production build command on Netlify doesn’t contain this --buildDrafts flag.

And of course, I delete the “draft” status of these posts when I’m ready to publish them.

Section 4: Faster Hugo content creation with VS Code

You can speed up your Hugo workflow when writing content in VS Code by using something called “VS Code User Snippets”. I’ll explain what they are below.

What are VS Code User Snippets?

VS Code User Snippets are pieces of text or code that you can write once, and reuse over and over without having to type it all out again.

They can also contain variables, such as the current date, which makes them extra powerful.

I use VS Code User Snippets in two main ways:

  • Quickly filling out the YAML front matter for each new Hugo post
  • Quickly inserting a Markdown image

I’ll cover each of these below.

How to use VS Code “User Snippets” to quickly insert Hugo’s YAML front matter

If you’re not familiar with Hugo’s YAML front matter, have a read of the box below—otherwise, feel free to skip the box.

As you may know, it can be a little repetitive adding the YAML front matter each time you start a new post. And while you can expedite this with Hugo archetypes, I prefer to do this with VS Code User Snippets. Here’s how I do this:

Step #1: Create a new User Snippet

Create a new User Snippet by clicking on Code > Preferences > Configure User Snippets.

Step #2: Choose the type of Snippet you want: global or local

You can now choose whether you want this new snippet to be global (i.e. across all your workspaces) or scoped to the current workspace only.

If you have multiple Hugo blogs with the same YAML front matter fields, then global scope might make sense.

However, if you only have one Hugo blog, or your YAML front matter fields vary across your blogs, then choose the local scope.

The global option is called New Global Snippets file..., while the workspace-scoped option is called New Snippets file for 'moonbooth'... (obviously it won’t say moonbooth, it will say the name of your workspace).

Step #3: Name your User Snippet

You can name your user snippet whatever you want. Let’s call ours yaml.

Step #4: Copy and paste the User Snippet below, changing where necessary

{
	"New Blog Post": {
		"prefix": ["blog-post"],
		"body": ["---\ntitle:\ndate: $CURRENT_YEAR-$CURRENT_MONTH-$CURRENT_DATE $CURRENT_HOUR:$CURRENT_MINUTE:$CURRENT_SECOND+11:00\nseoTitle:\ndescription:\nauthors: [\"YOUR NAME HERE\"]\n---"],
		"description": "New blog post for MoonBooth."
	}
}

That will give you the following output:

---
title:
date: 2020-03-06 18:23:32+11:00
seoTitle:
description:
authors: ["YOUR NAME HERE"]
---

You’ll obviously need to change the parts that don’t apply to you. Where it says YOUR NAME HERE, put your name (or that of your content person).

Do note that I’ve hardcoded in the timezone (+11:00) for Sydney, Australia (where I live), but you’ll probably need to change that, unless you live in my neck of the woods too, in which case, hello!

And of course, if you’re not using the YAML front matter fields I use, you’ll need to edit that too. seoTitle and authors are custom YAML front matter fields I’ve added, whereas title, description and date ship with Hugo.

Step #5: Create a new Hugo blog post

In the folder tree on the right of the VS Code interface, right-click on the Hugo /content/ folder, and clicking New File from the context menu. Give the file a name. Remember to include the Markdown extension, e.g. my-new-post.md.

Step #6: Summon your new User Snippet in your new blog post

There are (at least) two ways to do this.

The most efficient way is to simply type the User Snippet “prefix” (ours is yaml) anywhere in the Markdown file and hit Tab.

If you can’t remember what your prefix is, you can always hit Ctrl + Space at the same time. Make sure your cursor is in a blank part of your Markdown file (e.g. at the start of a new line).

You’ll then see a small overlay with your User Snippets.

Select the one you want (in this case, yaml), hit return, and it will be inserted into your blog post. Boom!

How to use VS Code “User Snippets” to quickly insert images into your Markdown posts

This process is very similar to the one above, but this time, we’re going to create a user snippet for inserting a Markdown image.

If you’re not sure how to (manually) insert an image in Markdown, take a look at the box below (and obviously feel free to skip it).

As with the YAML example above, it can be nice to save some keystrokes where possible. Also as with the YAML example, a great way to do this is with VS Code User Snippets.

Here’s how to do that:

Step #1: Create a new User Snippet

Create a new User Snippet by clicking on Code > Preferences > Configure User Snippets.

Step #2: Choose the type of Snippet you want: global or local

As with the YAML example, your choice here will depend on whether you plan to use this snippet in more than one workspace. If the answer is yes, then global scope is probably best; if not, local.

The global option is called New Global Snippets file..., while the workspace-scoped option is called New Snippets file for 'moonbooth'... (obviously it won’t say moonbooth, it will say the name of your workspace).

Step #3: Name your User Snippet

You can name your user snippet whatever you want. Let’s call ours img.

Step #4: Copy and paste the User Snippet below, changing where necessary

{
	"img": {
		"scope": "markdown",
		"prefix": ["image"],
		"body": ["![$1](/images/uploads/$2)"],
		"description": "New image"
	}
}

Step #5: Summon your new User Snippet in your new blog post

As in the other user snippet example, there are (at least) two ways to do this.

The most efficient way is to simply type the User Snippet “prefix” (ours is image) anywhere in the Markdown file and hit Tab.

If you can’t remember what your prefix is, you can always hit Ctrl + Space at the same time. Make sure your cursor is in a blank part of your Markdown file (e.g. at the start of a new line).

You’ll then see a small overlay with your User Snippets.

Select the one you want (in this case, image), hit return, and it will be inserted into your blog post. Boom!

How to have VS Code automatically trim trailing whitespace

You can have VS Code automatically trim trailing whitespace—this is something I like to do so I don’t get extraneous changes in my diffs when committing to GitHub.

You don’t need an extension for this—simply change your VS Code settings so that files.trimTrailingWhitespace is set to true (if using settings.json), or ensure the box marked “Files: Trim Trailing Whitespace” is checked (if using the settings UI).

How to have VS Code automatically trim final newlines

Similarly, I like to control newlines for the same reason: so I don’t get extraneous changes in my diffs when committing to git.

To do so, I have both of the following settings activated:

  1. “Files: Insert Final Newline” (files.insertFinalNewline)
  2. “Files: Trim Final Newlines” (files.trimFinalNewlines)

Activating both of these ensures my files all have one—and only one—final newline. This keeps my files consistent, and ensures I never have zero or multiple final newlines.

How to have VS Code trim non-final newlines

Another measure I like to take around governance of newlines is to one-click delete multiple blank lines between paragraphs.

I do this with an extension called “Remove Blank Lines”. It adds a setting named “Remove-empty-lines: Allowed Number Of Empty Lines” to VS Code, which is set to 1 by default. I keep it on this sensible default because it preserves the blank lines between paragraphs, but deletes any redundant empty lines—which have no meaning in Markdown.

To actually use the extension, you can use this two-part keyboard shortcut: Ctrl+Shift+Z followed by Ctrl+Shift+A (it’s Ctrl in both cases, even on a Mac).

You can actually make this a one-click command if you have a programmable keyboard or a macro board. I myself have a StreamDeck, which saves me a lot of time in VS Code.

Is VS Code’s nativeMarkdown validation worth using?

The September 2022 release of VS Code added support for native validation of Markdown. I was excited to try this, but I ended up turning it off when using it with Hugo. There were two reasons I did this:

VS Code’s native Markdown validation triggers false-positive errors for Hugo images

VS Code’s native Markdown validation checks—amongst other things—that the image source files you link to actually exist at the place you specify.

This is all well and good in theory, or perhaps for other applications, but it falls down for Hugo. Here’s why:

When you insert an image in Hugo, the Markdown looks something like this:

![Apollo 11](/images/apollo-11.jpg)

However, the actual location of the image—relative to your Hugo repo—is:

/static/images/apollo-11.jpg

Because Hugo abstracts away the /static folder, the VS Code Markdown validator gets tripped up when it detects a “mismatch”. So it throws a false positive for every single blog post with an image. Annoying, to say the least.

VS Code’s native Markdown validation triggers false-positive errors for the authors

In my post on how to use authors in Hugo, I explained that Hugo expects the value for the authors YAML front matter field to be an array, like this:

authors: ["Ron Erdos"]

This is true even if you only list one author, as in the example above.

However, VS Code’s native Markdown validation feature gets tripped up at the square brackets and throws an error, stating “No link definition found”. This is another reason I’m keeping VS Code’s native Markdown validation turned off when working with Hugo—for now.

Section 5: Publishing content to the web from within VS Code with git and CI/CD

I host my sites with Netlify, who provide an amazing service, even on my free plan.

I’ve set my Netlify config up so any time I push a commit to my main branch—either from VS Code’s source control UI, GitHub Desktop, or a terminal—Netlify rebuilds my site. I wrote a post on how to host Hugo sites on Netlify if you want to read more.

What this means in practice is that I can publish new content or edit existing content and get it live on the web without having to leave VS Code. The efficiency gains over time are amazing.

"Thanks so much for your work ... I'm migrating my WordPress blog to Hugo and it's been really helpful." — Francisco S., engineer and blogger

"I love your content. The information that you provide have been very useful in the development of my personal website." — Mattia C., engineer and researcher

The planets in our solar system