Documentation Index Fetch the complete documentation index at: https://buttercms.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
Overview
This integration guide shows you how to how to update your existing project to:
install the ButterCMS package
instantiate ButterCMS
create components to fetch and display each of the three ButterCMS content types: Pages , Collections , and Blog Posts .
Starter project
Or, you can jump directly to the starter project below, which will allow you to clone, install, run, and deploy a fully working
starter project that’s integrated with content already inside of your ButterCMS account.
Hugo Starter Project Hit the ground running with a pre-configured Hugo + ButterCMS setup.
Overview
Hugo doesn’t have native CMS integrations, so you’ll use ButterCMS by:
Fetching content at build time using a Node.js script
Saving content as Hugo data files or Markdown
Using Hugo templates to render the content
Installation
Add your API token to .env:
BUTTERCMS_API_TOKEN = your_api_token
Build configuration
{
"scripts" : {
"fetch" : "node scripts/fetch-content.js" ,
"generate" : "node scripts/generate-pages.js" ,
"prebuild" : "npm run fetch && npm run generate" ,
"build" : "hugo" ,
"dev" : "npm run prebuild && hugo server"
},
"dependencies" : {
"buttercms" : "^1.2.0" ,
"dotenv" : "^16.0.0"
}
}
For complete SDK documentation including all available methods and configuration options, see the JavaScript SDK Reference .
Pages
Fetch Script
Generate Script
// scripts/fetch-content.js
require ( 'dotenv' ). config ();
const Butter = require ( 'buttercms' );
const fs = require ( 'fs' );
const path = require ( 'path' );
const butter = Butter ( process . env . BUTTERCMS_API_TOKEN );
async function fetchContent () {
const dataDir = path . join ( __dirname , '../data' );
if ( ! fs . existsSync ( dataDir )) {
fs . mkdirSync ( dataDir , { recursive: true });
}
// Fetch pages
const pages = await butter . page . list ( 'landing-page' );
fs . writeFileSync (
path . join ( dataDir , 'pages.json' ),
JSON . stringify ( pages . data . data , null , 2 )
);
// Fetch blog posts
const posts = await butter . post . list ({ page_size: 100 });
fs . writeFileSync (
path . join ( dataDir , 'posts.json' ),
JSON . stringify ( posts . data . data , null , 2 )
);
// Fetch collections
const collections = await butter . content . retrieve ([ 'brands' , 'navigation' ]);
fs . writeFileSync (
path . join ( dataDir , 'collections.json' ),
JSON . stringify ( collections . data . data , null , 2 )
);
console . log ( 'Content fetched successfully!' );
}
fetchContent (). catch ( console . error );
// scripts/generate-pages.js
const fs = require ( 'fs' );
const path = require ( 'path' );
const dataDir = path . join ( __dirname , '../data' );
const contentDir = path . join ( __dirname , '../content' );
// Generate landing pages
const pages = JSON . parse ( fs . readFileSync ( path . join ( dataDir , 'pages.json' )));
const pagesDir = path . join ( contentDir , 'page' );
if ( ! fs . existsSync ( pagesDir )) {
fs . mkdirSync ( pagesDir , { recursive: true });
}
pages . forEach ( page => {
const seo = page . fields . seo || {};
const content = `---
title: " ${ page . fields . headline } "
description: " ${ seo . description || '' } "
slug: " ${ page . slug } "
date: ${ new Date (). toISOString () }
hero_image: " ${ page . fields . hero_image || '' } "
---
${ page . fields . body || '' }
` ;
fs . writeFileSync ( path . join ( pagesDir , ` ${ page . slug } .md` ), content );
});
console . log ( 'Pages generated successfully!' );
Page template
<!-- layouts/page/single.html -->
{{ define "main" }}
< article >
< h1 > {{ .Title }} </ h1 >
{{ if .Params.hero_image }}
< img src = "{{ .Params.hero_image }}" alt = "{{ .Title }}" />
{{ end }}
{{ .Content }}
</ article >
{{ end }}
Collections
Collections are automatically fetched to data/collections.json. Access them in templates:
<!-- layouts/brands/list.html -->
{{ define "main" }}
< main >
< h1 > Our Brands </ h1 >
< ul >
{{ range .Site.Data.collections.brands }}
< li >
< img src = "{{ .logo }}" alt = "{{ .name }}" />
< h2 > {{ .name }} </ h2 >
{{ .description | safeHTML }}
</ li >
{{ end }}
</ ul >
</ main >
{{ end }}
Dynamic components
Component partial
<!-- layouts/partials/components/hero.html -->
< section class = "hero" >
< h1 > {{ .headline }} </ h1 >
< p > {{ .subheadline }} </ p >
{{ if .button_label }}
< a href = "{{ .button_url }}" class = "btn" > {{ .button_label }} </ a >
{{ end }}
{{ if .image }}
< img src = "{{ .image }}" alt = "{{ .headline }}" />
{{ end }}
</ section >
Component renderer
<!-- layouts/partials/component-renderer.html -->
{{ range . }}
{{ if eq .type "hero" }}
{{ partial "components/hero.html" .fields }}
{{ else if eq .type "features" }}
{{ partial "components/features.html" .fields }}
{{ else if eq .type "cta" }}
{{ partial "components/cta.html" .fields }}
{{ end }}
{{ end }}
Using in templates
Store component data in page front matter during generation, then render:
<!-- layouts/landing/single.html -->
{{ define "main" }}
< main >
{{ $slug := .Params.slug }}
{{ range .Site.Data.pages }}
{{ if eq .slug $slug }}
{{ if .fields.body }}
{{ partial "component-renderer.html" .fields.body }}
{{ end }}
{{ end }}
{{ end }}
</ main >
{{ end }}
Blog
Generate Blog Posts
Blog List Template
Blog Post Template
// scripts/generate-pages.js (add to existing)
const posts = JSON . parse ( fs . readFileSync ( path . join ( dataDir , 'posts.json' )));
const postsDir = path . join ( contentDir , 'blog' );
if ( ! fs . existsSync ( postsDir )) {
fs . mkdirSync ( postsDir , { recursive: true });
}
posts . forEach ( post => {
const content = `---
title: " ${ post . title } "
slug: " ${ post . slug } "
date: ${ post . published }
author: " ${ post . author . first_name } ${ post . author . last_name } "
featured_image: " ${ post . featured_image || '' } "
summary: " ${ post . summary ?. replace ( /"/ g , ' \\ "' ) || '' } "
categories: [ ${ post . categories ?. map ( c => `" ${ c . name } "` ). join ( ', ' ) || '' } ]
tags: [ ${ post . tags ?. map ( t => `" ${ t . name } "` ). join ( ', ' ) || '' } ]
---
${ post . body }
` ;
fs . writeFileSync ( path . join ( postsDir , ` ${ post . slug } .md` ), content );
});
<!-- layouts/blog/list.html -->
{{ define "main" }}
< main >
< h1 > Blog </ h1 >
< ul >
{{ range .Pages.ByPublishDate.Reverse }}
< li >
< h2 >< a href = "{{ .Permalink }}" > {{ .Title }} </ a ></ h2 >
< p > By {{ .Params.author }} on {{ .Date.Format "January 2, 2006" }} </ p >
< p > {{ .Params.summary }} </ p >
</ li >
{{ end }}
</ ul >
{{ template "_internal/pagination.html" . }}
</ main >
{{ end }}
<!-- layouts/blog/single.html -->
{{ define "main" }}
< article >
< h1 > {{ .Title }} </ h1 >
< p > By {{ .Params.author }} on {{ .Date.Format "January 2, 2006" }} </ p >
{{ if .Params.featured_image }}
< img src = "{{ .Params.featured_image }}" alt = "{{ .Title }}" />
{{ end }}
{{ .Content }}
< a href = "/blog" > Back to Posts </ a >
</ article >
{{ end }}
Webhooks for rebuilds
Set up webhooks to trigger rebuilds when content changes:
Go to Settings → Webhooks in ButterCMS
Add your build hook URL:
Netlify : Site Settings → Build & Deploy → Build hooks
Vercel : Project Settings → Git → Deploy Hooks
Cloudflare Pages : Configure a webhook trigger
Select events: Page Published, Post Published, etc.
SEO
<!-- layouts/partials/seo.html -->
{{ $seo := .Params.seo | default dict }}
< title > {{ $seo.title | default .Title }} </ title >
< meta name = "description" content = "{{ $seo.description | default .Description }}" >
<!-- Open Graph -->
< meta property = "og:title" content = "{{ $seo.og_title | default $seo.title | default .Title }}" >
< meta property = "og:description" content = "{{ $seo.og_description | default $seo.description | default .Description }}" >
{{ if $seo.og_image }}
< meta property = "og:image" content = "{{ $seo.og_image }}" >
{{ end }}
<!-- Twitter -->
< meta name = "twitter:card" content = "summary_large_image" >
< meta name = "twitter:title" content = "{{ $seo.title | default .Title }}" >
< meta name = "twitter:description" content = "{{ $seo.description | default .Description }}" >
Hugo configuration
# config.toml
baseURL = "https://example.com"
languageCode = "en-us"
title = "My ButterCMS Site"
[ params ]
description = "A Hugo site powered by ButterCMS"
[ taxonomies ]
category = "categories"
tag = "tags"
[ pagination ]
pagerSize = 10
Resources
JavaScript SDK Complete SDK reference
Webhooks Set up content webhooks
GitHub Repository View source code
Content API REST API documentation