In this article, I’ll show you how to feed a Hugo site data from a headless CMS so developers can reap the benefits of a static site while allowing content creators to use the tools they love.
How “React burnout” led me to Hugo
I started looking at other options to avoid burnout. Hugo’s popularity in the State of Jamstack 2020 Report piqued my interest, so I watched some tutorials. It seemed straightforward, so I fired up a "quick start" tutorial and experimented. Within two weeks, I went from zero Hugo experience to a working site pulling content from my headless CMS of choice! What a difference compared to my first encounter with React!
What is Hugo?
Hugo takes a source directory of files and templates and uses these as input to create a complete website. It’s built on GO, has its own templating language, and it can be hosted anywhere. The Hugo team (boldly) claims that it’s the world’s fastest framework for building websites, one of the most popular open-source static site generators, and sports amazing speed and flexibility.
What does that mean to you?
You get the benefits of a statically generated site without having to learn some advanced React concepts or complicated JS. You don’t even really need to know how to program in GO. Armed with just some basic Hugo templating concepts, HTML/CSS, and knowledge about Hugo’s directory conventions is enough to build a site.
In my experience, it is fast and simple, but I found two drawbacks:
- There is no ability to truly create dynamic pages out of the box. To use Hugo’s out-of-the-box features like handling routing, content needs to be physically present in the “content” directory. This means by default pages created in external systems (like a CMS) can’t be dynamically added to the project.
- Hugo uses physical markdown files with limited content creation tools.
What do those limitations mean to your non-technical team?
Not everyone likes Markdown. I’d argue most content creators want a more graphical experience than hammering # or * symbols into a terminal-like editor. They also miss out on valuable CMS features that they’ve grown accustomed to like workflows or collaborative editing. Worst of all, in Hugo, they would have to make changes directly to markdown files that live alongside code files (😱).
So how can you bridge the gap between your team’s content creation needs and your desire to use Hugo?
Never fear, the CMS is here!
The key piece for making both developers and content creators happy with Hugo is integrating a headless CMS. A headless CMS provides content creation tools, like editorial calendars:
and collaboration features like Simultaneous Editing:
Quality-of-life features like those make content creators and editors happy. What about you as the developer? Well, you don’t have to host it, as it’s cloud-based, and you get an SDK for simple data gathering. Doesn’t that sound great?
I’ll summarize my solution in 5 steps:
- Setting up Hugo
- Connecting Hugo to the headless CMS
- Converting JSON to Markdown
- Using build scripts
- Deploying the site
Setting up Hugo
Once Hugo was installed, I ran the new site command:
This created a new Hugo site with all the boilerplate directories necessary to get started quickly. I created a new markdown file using the commands:
And I added markdown content to the newly created file:
Once the test content had been created and the structure was established, I added a _default sub-directory to the layout directory and created single.html and list.html layouts:
Running the command hugo server started a server to test my sample content with.
With the basics up and running, I could get started on integrating the headless CMS.
Connecting Hugo to the headless CMS
To get up and running quickly, I opted to use Kentico Kontent’s sample project and focused on grabbing the pre-made articles it contains.
npm i rxjs --save
npm i @kentico/kontent-delivery --save
Once the libraries were installed, I created a cms-scripts directory in the project to separate the CMS implementation details from Hugo’s.
From here, it was a matter of following the Kontent Delivery SDK’s documentation for setting up a Delivery Client and retrieving my Kontent project’s articles over the API. I separated this logic into two files:
node cms-scripts/buildArticles.js in the terminal resulted in a JSON response from the Kontent Delivery endpoint. Now it was time to transform the response into the physical markdown files Hugo expects.
Converting JSON to Markdown
There are three layers to converting Kontent’s JSON to Hugo’s default markdown file format:
Extracting the content from the response:
Creating a markdown converter that uses Turndown.js (
npm install turndown) to change extracted JSON content into markdown:
Creating a physical file in the Hugo content directory or sub-folders using Node’s built-in fs library:
cms-scripts/buildArticles.js (complete code):
Once the above scripts were in place, which can be seen in this GitHub repository, I was able to start populating my Hugo project with content by running
node cms-scripts/buildArticles.js in the terminal again.
With the markdown files in the /content/articles directory, I could bring the site online by running the command
Using build scripts
At this point, I had a locally running Hugo site that used my headless CMS content. Great! But manually running
node cms-scripts/buildArticles.js to populate my project followed by a separate command to run the Hugo server wasn’t ideal. To streamline this process, I used npm-run-all, which allowed me to run the scripts together on my Windows machine using a
local:start command I added to package.json.
In addition to simplifying the local build, this sets the groundwork for automating builds in a hosted environment.
Deploying the site
The final obstacles before being completely happy with my Hugo project were:
- deploying the site for the whole world to see
- automating the build process
Luckily, both are made possible with hosting services like Netlify. Not only is deploying to Netlify as easy as connecting to your GitHub repository, it also lets you run automated builds of your website with each Git push.
To leverage Netlify for hosting and automated builds, I modified my package.json to include a Netlify build script:
This made it possible to set my "Build & Deploy build command" in Netlify. I created a new site from Git, set the build command to
npm run netlify:build, and targeted
publish as the Publish directory, all from within the Netlify interface. Now, any code change in my GitHub repository for this project resulted in an automatic rebuild of the site.
To extend this automation to content updates in the CMS, I used Netlify’s build hooks paired with Kentico Kontent’s webhooks per the Kontent documentation. With this addition, whenever the content is published or unpublished in my Kentico Kontent project, Netlify rebuilds my site so the content is always up to date.
In this article, I introduced Hugo, discussed how to connect Hugo to a headless CMS, and how doing so benefits both developers and content creators. I hope that I showed you that both you and your non-technical teammates can enjoy working with Hugo in future projects!
If this article got you excited about the possibilities of Hugo and Kentico Kontent, a more robust Hugo + Kontent sample site can be seen here.