Organize Your Hugo Content: Generating Subfolder Pages with a Prebuild Script
Hugo, a popular static site generator, offers a flexible way to structure your content. While it's common to place Markdown files directly within the content
directory, organizing content into subfolders can lead to a cleaner and more manageable website structure. This article will guide you through a method to automatically generate .md
files within distinct subfolders (like articles
and projects
) using a Hugo prebuild script.
The Challenge: Centralized Content Management
When fetching content from external sources (like a CMS via an API), you might want to categorize different content types into separate sections on your website. Manually creating these folders and placing the generated Markdown files can become tedious.
The Solution: A Unified Prebuild Script
The provided prebuild.html
template leverages Hugo's templating capabilities and the resources.GetRemote
function to fetch data from two different API endpoints and organize them into respective subfolders.
Here's a breakdown of the script ( where Bearer Token and api url are saved in the config.toml file):
{{ $bearer := (printf "Bearer %s" site.Params.api_token) }} {{ $opts := dict "headers" (dict "Authorization" $bearer) }}{{ $articlesUrl := (printf "%s/items/articles?sort=-id&random=4" site.Params.api_url) }} {{ with resources.GetRemote $articlesUrl $opts | transform.Unmarshal }} {{ range index .data }} {{ $meta := dict "slug" .slug "title" .title "tags" .tags }} {{ $metaJson := jsonify $meta }} {{ $content := .long_description | safeHTML }} {{ $output := printf "%s\n%s" $metaJson $content }} {{ $filename := printf "content/articles/%s.md" .slug }} {{ $resource := resources.FromString $filename $output }} {{ $file := $resource.RelPermalink }} {{ end }} {{ end }} {{ $projectsUrl := (printf "%s/items/projects?sort=sort&random=2" site.Params.api_url) }} {{ with resources.GetRemote $projectsUrl $opts | transform.Unmarshal }} {{ range index .data }} {{ $meta := dict "slug" .slug "title" .title "tags" .tags }} {{ $metaJson := jsonify $meta }} {{ $content := .long_description | safeHTML }} {{ $output := printf "%s\n%s" $metaJson $content }} {{ $filename := printf "content/projects/%s.md" .slug }} {{ $resource := resources.FromString $filename $output }} {{ $file := $resource.RelPermalink }} {{ end }} {{ end }}
Key aspects of this solution:
Directory Structure: Instead of placing all files directly in the
content
folder, the script defines specific output paths:content/articles/[slug].md
for articlescontent/projects/[slug].md
for projects
Combined Script: A single
index.html
template handles fetching and generating content for both "articles" and "projects" based on their respective API endpoints.
Implementation Steps
To integrate this into your Hugo site:
- Inside the main folder of your project create the
prebuild/layouts
directory: If it doesn't already exist in your Hugo project. - Place
prebuild.html
: Save the provided code asprebuild/layouts/index.html
. - Run
hugo --minify
in prebuild folder - Map the prebuild public folder inside your config.toml
[module.mounts]
source = "prebuild/public/content"
target = "content" - Run
hugo --minify
in main project folder
Now, when you build your Hugo site, this prebuild script will fetch your articles and projects and automatically place their corresponding .md
files in the content/articles
and content/projects
folders, respectively.
Benefits of this Approach
- Organized Content: Clear separation of content types within your
content
directory. - Simplified Management: A single script handles the generation for multiple content types.
- Clean URLs: Hugo will automatically create clean URLs based on the subfolder structure (e.g.,
/articles/your-article-slug/
and/projects/your-project-slug/
).
This method provides an efficient way to manage and structure your Hugo content when sourcing data from external APIs. You can easily extend this approach to handle more content types by adding additional fetch and generation blocks to the prebuild/layouts/index.html
file.