A themed demo of this site can be seen here.
Sample Hugo static site generator project using the Kentico Kontent Delivery JavaScript SDK to retrieve content.
This application is meant for use with the Dancing Goat sample project within Kentico Kontent. If you don't have your own Sample Project, any administrator of a Kentico Kontent subscription can generate one.
- Application Setup
- How It Works
- Setting up Webhooks
- Using a Theme
- Content Administration
- Deploying to Netlify
- Getting Support
To run the app:
- Clone the app repository with your favorite GIT client
- Open the solution in Visual Studio Code or your favorite IDE
- Update the Kontent project ID in
.env
- detailed instructions available below - From the terminal run:
npm install
npm run cms:prepare
npm run local:start
- Running
npm run cms:prepare
creates markdown copies of the Dancing Goat sample articles in thecontent/articles
directory. - Running
npm run local:start
starts the Hugo server onhttp://localhost:1313
and a webhook endpoint athttp://localhost:3000
. Information regarding webhooks can be seen in the Setting up webhooks section. - To run a styled version of the site follow steps in the Using a theme section.
Alternatively, you can run npm run cms:prepare && local:serve
in the terminal to run just the Hugo site without the webhook functionality.
If you already have a Kentico Kontent account, you can connect this application to your version of the Sample project.
- In Kentico Kontent, choose Project settings from the app menu
- Under Development, choose API keys and copy the Project ID
- Open the
.env
file - Use the values from your Kentico Kontent project as the PROJECT_ID value
- Save the changes
- Run the application from the terminal
To follow the recommended content organization from the official Hugo documentation, content for this project is stored in the /content
directory in a subfolder matching the content type (e.g., "articles" in this sample). Additionally, content is stored in the markdown (.md) format to leverage Hugo's default content management features and fast markdown parsing with minimal custom configuration.
Pulling content from Kentico Kontent and making it usable by Hugo can be broken down into three steps:
- One: Consuming Content from the API
- Two: Converting Kentico Kontent JSON to Markdown
- Three: Creating the Physical Markdown files
handled by two files in cms-scripts: buildArticles.js and markdownConverter.js.
The cms:prepare
command uses the Kentico Kontent Delivery JavaScript SDK to return all "article" content items from Kentico Kontent:
//code from cms-scripts/buildArticles.js
const { deliveryClient } = require('./config')
//... additional requires removed for brevity
const subscription = deliveryClient.items()
.type('article')
.depthParameter(2)
.queryConfig({
urlSlugResolver: resolveLinkInRichText,
richTextResolver: resolveItemInRichText
})
.toObservable()
.subscribe(/*...*/)
Since the Kentico Kontent Delivery JavaScript SDK responds in the JSON format, it's necessary to loop through the items returned in step one and have their respective title, date, body_copy, and teaser_image
converted to the markdown format using Turndown.js.
//code continued from cms-scripts/buildArticles.js
//...
const markdownConverter = require('./markdownConverter');
//...
.subscribe(response => {
for (var item of response.items){
//frontmatter example:
const title = item.title.value
const body_copy = item.body_copy.resolveHtml()//resolveHtml to resolve rich text markup
//... additional elements removed for brevity
//convert JSON values to markdown
const data = markdownConverter.convert(title, date, body_copy, teaser_image)
//code from cms-scripts/markdownConverter.js
const convert = (title, date, body_copy, teaser_image) => {
//markdown conversion
const turndownService = new TurndownService()
const markdown = turndownService.turndown(body_copy)
const header_image = turndownService.turndown(`<img src="${teaser_image.url}" alt="${teaser_image.description}"/>`)
const data = `---
title: "${title}"
date: ${date}
draft: false
---
${header_image}
${markdown}
`
return data
}
With the JSON data converted to markdown, the final step is to create the physical content markdown files Hugo uses to render the site.
//code continued from cms-scripts/buildArticles.js
const fs = require('fs');
//...
.subscribe(response => {
for (var item of response.items){
//... code emitted for brevity
fs.writeFileSync(`content/articles/${codename}.md`, data)
}
The API query used in cms-scripts\buildArticles.js is set to resolve links and inline content items on the query level as described in the Kentico Kontent JavaScript SDK documentation here.
//code from cms-scripts/buildArticles.js
const { resolveItemInRichText } = require('./itemResolver');
const { resolveLinkInRichText } = require('./linkResolver');
//... additional requires removed for brevity
const subscription = deliveryClient.items()
.type('article')
.depthParameter(2)
.queryConfig({
urlSlugResolver: resolveLinkInRichText,
richTextResolver: resolveItemInRichText
})
.toObservable()
.subscribe(/*...*/)
Link resolution is configured in cms-scripts/linkResolver.js to evaluate the "link type" of links returned in the buildArticles.js
API call, then return a url property handled by the JavaScript SDK.
//code from cms-scripts/linkResolver.js
const resolveLinkInRichText = (link, context) => {
if (link.type === 'article'){
return { url: `/articles/${link.codename}`};
}
return { url: 'unsupported-link'};
}
exports.resolveLinkInRichText = resolveLinkInRichText;
Content item resolution is configured in cms-scripts/itemResolver.js to evaluate the content type of inline content items returned in the buildArticles.js
API call, then return a Hugo shortcode that matches the type. Hugo natively supports tweets, vimeo, and youtube videos used in the sample.
//code from cms-scripts/itemResolver.js
const resolveItemInRichText = (item) => {
//... code emitted for brevity
//"host_video" correlates with a content type in Kentico Kontent
if (item.system.type === 'hosted_video'){
let video_id = item.video_id.value;
let host_name = item.video_host.value[0].name;
//"host_name" correlates with a content type element set in Kentico Kontent
if(host_name === 'YouTube'){
return `{{< youtube ${video_id} >}}`
}
else if(host_name === 'Vimeo') {
return `{{< vimeo ${video_id} >}}`
}
else {
return `> Video unavailable.`
}
}
return `> Content not available.`
}
exports.resolveItemInRichText = resolveItemInRichText;
Executing the inline resolution requires calling the JavaScript SDK's resolveHTML()
method on the rich text element containing the inline content items.
//code continued from cms-scripts/buildArticles.js
//...
//article content
const body_copy = item.body_copy.resolveHtml()
Examples of resolved links and content items can be seen in the "Coffee Beverages Explained" article.
Webhooks can be used to create or remove markdown files from content\articles
when an article is published or unpublished in Kentico Kontent. The URL of the application needs to be publicly accessible, e.g. https://myboilerplate.azurewebsites.net/hook or by using a tunneling/routing service like ngrok:
- Create a webhook in Kentico Kontent and point it to your application's domain with "/hook" appended to the URL. Example: https://8dbe14c768be.ngrok.io/hook
- Ensure that the webhook is setup for the "Publish" and "Unpublish" Delivery API triggers
- Open the sample site in your IDE
- Run
npm run local:start
in the terminal to run both the site and webhook - Make an edit to an article in Kentico Kontent and promote it through the workflow to the "Published" step
Reference the Kentico Kontent documentation.
Hugo has a large list of available themes that can be applied to your site and modified to fit your needs. This site uses a forked version of the Pickles theme with modified index.html
and head.html
layouts.
To add the Pickles theme, in the terminal run:
local:prepare-themed
To run the site locally with the Pickles theme run:
"local:serve-themed"
(without webhooks) OR"local:start-themed"
(with webhooks. See the: Setting up webhooks section)
Note: the local:prepare-themed
command renames the root layout/_default
folder to layout/default
to allow the downloaded theme to override the default layout files.
To run the themeless version:
- Ensure that
layout/_default
exists in the root layout folder - Run
npm run local:start
- Navigate to https://app.kontent.ai in your browser.
- Sign in with your credentials.
- Manage content in the content administration interface of your sample project.
You can learn more about content editing with Kentico Kontent in our Documentation.
Note: This project is only setup to support the "Article" content type from the Kentico Kontent Sample project.
When deploying to Netlify, set:
- Build Command:
npm run netlify:build
- Publish Directory:
public
as well as an environmental variable:
- PROJECT_ID: your_project_id (see Connecting to your sample project)
Ways to contribute and where to get support can be seen in the contributing guidelines here.