> ## 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.

# Hugo

> Build static sites with Hugo and ButterCMS. Covers setup, fetching content at build time, and rendering pages, collections, and blog posts.

## 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](../../core-concepts/content-types/pages), [Collections](../../core-concepts/content-types/collections), and [Blog Posts](../../core-concepts/content-types/blog-engine).

<Tip>
  In order for the snippets to work, you'll need to [setup your dashboard content schemas inside of ButterCMS](../buttercms-setup) first.
</Tip>

### 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.

<Card title="Hugo Starter Project" icon="rocket" href="../starter-projects/hugo">
  Hit the ground running with a pre-configured Hugo + ButterCMS setup.
</Card>

## 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

<Tabs>
  <Tab title="npm">
    ```bash theme={null}
    npm install buttercms
    ```
  </Tab>

  <Tab title="yarn">
    ```bash theme={null}
    yarn add buttercms
    ```
  </Tab>

  <Tab title="pnpm">
    ```bash theme={null}
    pnpm add buttercms
    ```
  </Tab>
</Tabs>

Add your API token to `.env`:

```bash theme={null}
BUTTERCMS_API_TOKEN=your_api_token
```

## Build configuration

```json theme={null}
{
  "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"
  }
}
```

<Info>
  For complete SDK documentation including all available methods and configuration options, see the [JavaScript SDK Reference](../sdks/javascript-sdk).
</Info>

## Pages

<Tabs>
  <Tab title="Fetch Script">
    ```javascript theme={null}
    // 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);
    ```
  </Tab>

  <Tab title="Generate Script">
    ```javascript theme={null}
    // 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!');
    ```
  </Tab>
</Tabs>

### Page template

```html theme={null}
<!-- 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:

```html theme={null}
<!-- 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

```html theme={null}
<!-- 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

```html theme={null}
<!-- 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:

```html theme={null}
<!-- 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

<Tabs>
  <Tab title="Generate Blog Posts">
    ```javascript theme={null}
    // 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);
    });
    ```
  </Tab>

  <Tab title="Blog List Template">
    ```html theme={null}
    <!-- 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 }}
    ```
  </Tab>

  <Tab title="Blog Post Template">
    ```html theme={null}
    <!-- 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 }}
    ```
  </Tab>
</Tabs>

## Webhooks for rebuilds

Set up webhooks to trigger rebuilds when content changes:

1. Go to **Settings** → **Webhooks** 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

```html theme={null}
<!-- 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

```toml theme={null}
# 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

<CardGroup cols={2}>
  <Card title="JavaScript SDK" icon="js" href="../sdks/javascript-sdk">
    Complete SDK reference
  </Card>

  <Card title="Webhooks" icon="bell" href="../../api/concepts/webhooks-overview">
    Set up content webhooks
  </Card>

  <Card title="GitHub Repository" icon="github" href="https://github.com/ButterCMS">
    View source code
  </Card>

  <Card title="Content API" icon="database" href="../../api-reference/pages/get-multiple-pages">
    REST API documentation
  </Card>
</CardGroup>
