Skip to main content

Overview

This integration guide shows you how to how to update your existing project to:
  1. install the ButterCMS package
  2. instantiate ButterCMS
  3. create components to fetch and display each of the three ButterCMS content types: Pages, Collections, and Blog Posts.
In order for the snippets to work, you’ll need to setup your dashboard content schemas inside of ButterCMS first.

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:
  1. Fetching content at build time using a Node.js script
  2. Saving content as Hugo data files or Markdown
  3. Using Hugo templates to render the content

Installation

npm install buttercms
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

// 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);

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

// 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);
});

Webhooks for rebuilds

Set up webhooks to trigger rebuilds when content changes:
  1. Go to SettingsWebhooks in ButterCMS
  2. Add your build hook URL:
    • Netlify: Site Settings → Build & Deploy → Build hooks
    • Vercel: Project Settings → Git → Deploy Hooks
    • Cloudflare Pages: Configure a webhook trigger
  3. 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