Hugo

Images alongside content

We use Hugo to render our docs. Our documentation lives alongside the source for our project in one monorepo.
We’re trying to format the content so that it’s ergonomic for people to edit and view from within the source tree as well as rendering nicely to our docs site.

For non-page-bundle pages this works well, some_design.md gets a nice URL in the rendered docs. However when we want to use images inside these pages we’ve ended up moving the markdown to some_design/index.md and putting its images alongside it as a page bundle. I did this to prevent document authors from having to know about the output directory structure, so they wouldn’t have to reference images one level up from where the page was generated.

This isn’t that ergonomic for readers or writers of the documentation. People have to know to more about the output structure of the documentation, or about page bundles, than we’d like them to.

Is there a way to make image references “just work” for some_page.md if the image is in the same directory?

Not out of the box, but it is doable with a bit of creative thinking.

There are more than one ways to do this but I am going to cover the simpler one.


The other two ways are via the use of:

  • Hugo Modules (but that will require a Go installation for the project which you may or may not want, typically I don’t want the Go dependency so I do not use Hugo Modules)

  • Use resources. Grab the Markdown image from the .Content variable with some regex and store its PATH into a variable. Then generate a resource with resources.Get (not trivial and not for the faint of heart, but tonight I do not have the time for such adventures)


In any case there are a couple of configuration settings that you need to enter in your Docs project config.

BTW I had a look at your repo and I didn’t find a config.toml or yaml for the project. Are you generating the config via some script before deploying the site?


Anyhow the parameters are the following:

contentDir = "content"
assetDir = "content"
staticDir = ["content", "static"]

These parameters are self explanatory and if you’re not familiar with them you can look them up at the Hugo Docs.

Basically I am consolidating the static, asset and content folders into a single one.

Then in the template I would simply do this:

{{ $img := print `img src="/` .File.Dir `/` }}

{{ .Content | replaceRE `img src="` $img | safeHTML }}

Basically I am constructing the PATH to the img and storing it in a variable with .File.Dir:

.File.Dir

given the path content/posts/dir1/dir2/ , the relative directory path of the content file will be returned (e.g., posts/dir1/dir2/ ). Note that the path separator ( \ or / ) could be dependent on the operating system.


Also note how I am using backticks to escape the various characters that would cause issues with the Go templates and throw console errors -much more elegant way than using backslashes or whatever else IMO-.


And then I am performing the replacement within the .Content variable for every img that is contained within it.

That is how I would go about it. (With sheer brute force that is).

Performance wise this technique shouldn’t slow things down too much.

Unless of course, your project ends up with images into the hundreds or thousands (then I don’t know how slow things might get, you may want to explore one of the other options that I didn’t cover).