Migration from single content files to Hugo page bundles
Since I migrated from Jekyll to Hugo a few years ago, I have never looked at any other static site generator. I elaborated on this process in “From Jekyll to Hugo! From GitHub Pages to Netlify!”. It may not be the easiest tool of its kind out there, but the complexity pays off in the form of flexibility, speed, and reliability.
Over the last few years, I have been writing and adding more Markdown files to the content pile, as well as images to the static resources folder. It was all good, but I wasn’t fully utilizing the potential of Hugo. Finally, I decided to migrate from individual Markdown files to Hugo page bundles. For those unfamiliar with this concept, the following simple diagram should help.
// BEFORE
.
├── content
│ └── posts
│ ├── 2023-10-04-post-one.md
│ └── 2023-10-05-post-two.md
└── static
├── post-one-pic-1.jpg
├── post-one-pic-2.jpg
├── post-two-pic-1.jpg
└── post-two-pic-2.jpg
// AFTER
.
└── content
└── posts
├── 2023-10-04-post-one
│ ├── index.md
│ ├── post-one-pic-1.jpg
│ └── post-one-pic-2.jpg
└── 2023-10-05-post-two
├── index.md
├── post-two-pic-1.jpg
└── post-two-pic-2.jpg
Benefits of Page Bundles
The obvious benefit is file colocation. All files related to a single post live in the same directory. It vastly eliminates the potential for orphaned static files. When you delete a draft that you’ve lost hope for (which happens to me all the time), all related files will also be deleted. And because all links to images follow real relative paths, Markdown previews finally render correctly. A nice bonus!

Page resources is a Hugo concept where all files in a page bundle are accessible in a template via {{ .Resources }}
. They also have some extra image processing powers that are impossible with static files. This is very powerful and has allowed me to massively simplify how I handle multiple file formats (jpg
, webp
, and avif
) and dark mode variations. I have also managed to eliminate some unnecessary logic for handling open graph images—I need to remember to include an og.jpg
file in the page bundle, and Hugo will take care of the rest.
This single change doesn’t look like a lot to the visitor, but it was a massive improvement for me as a publisher. I can also reveal that I am planning to translate some of my posts into Polish, and with page bundles, it will be a breeze.
If you’re curious about how it works, the source code for my website is available on GitHub. Shout out if you have any questions or suggestions. Peace ✌️
Well done Paweł. How long it took you to migrate all your posts into page bundles (roughly)? This is something on my list to do, but the time involved pushing this more and more...
It was actually a very quick process. I wrote a small Node.js script that moved all the files to a new location for me. Writing the script didn't take me longer than 30 minutes, and the script execution took only a few hundred milliseconds. I would be delighted to assist you in creating a similar script if you need it. It was a fun experience 🤗
Thanks. If you can share yours I would appreciate it. I will look and see what I can do on my site :)
For the file structure and naming convention I followed, a simple script similar to this one did the job. Keep in mind that this is not exactly the code I used because I removed this throwaway script after I was done with it. It should be enough to give you an idea, though.
After that, I ran a find/replace in VSCode to amend the path for all images across all posts, and that was it. Hopefully, that helps.
I am so grateful for your comment about the script that simplified this for you. I was re-bundling after briefly un-bundling, and was about to do it manually for over 300 posts scattered across five years’ worth of folders; then I saw your comment, did a mental facepalm, and realized a script was the way to go. (Only difference was that I was much lazier than you, and just prompted Google Bard to write a Bash script for me. Amazingly, it worked as prescribed, no hallucinations, no muss, no fuss.) Thanks!