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

# Go

> Complete guide to integrating ButterCMS with Go for pages, collections, components, and blog

## 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="Go Starter Project" icon="rocket" href="../starter-projects/go">
  Hit the ground running with a pre-configured Go + ButterCMS setup.
</Card>

## Installation

```bash theme={null}
go get github.com/buttercms/buttercms-go
```

Set your API token as an environment variable:

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

## Initialize the Client

<Tabs>
  <Tab title="Standard Library">
    ```go theme={null}
    // main.go
    package main

    import (
        "os"
        "github.com/buttercms/buttercms-go"
    )

    func init() {
        ButterCMS.SetAuthToken(os.Getenv("BUTTERCMS_API_TOKEN"))
    }
    ```
  </Tab>

  <Tab title="Gin Framework">
    ```go theme={null}
    // main.go
    package main

    import (
        "os"
        "github.com/buttercms/buttercms-go"
        "github.com/gin-gonic/gin"
    )

    func init() {
        ButterCMS.SetAuthToken(os.Getenv("BUTTERCMS_API_TOKEN"))
    }

    func main() {
        r := gin.Default()
        r.LoadHTMLGlob("templates/*")
        // Routes defined below
        r.Run(":8080")
    }
    ```
  </Tab>
</Tabs>

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

## Pages

<Accordion title="ButterCMS Dashboard Setup">
  **Create a Page Type:**

  1. Go to **Content Types** → **Page Types** → Click **+**
  2. Add fields to your schema:
     * **Short Text** field named `headline`
     * **Long Text** field named `subheadline`
     * **Media** field named `hero_image`
     * **WYSIWYG** field named `body`
  3. Click **Create Page Type** and name it `Landing Page` (slug: `landing_page`)

  **Create a Page:**

  1. Go to **Pages** → **New Page** → Select **Landing Page**
  2. Enter a title (e.g., "Home") which generates slug `home`
  3. Fill in your content and click **Publish**
</Accordion>

<Tabs>
  <Tab title="Standard Library">
    ```go theme={null}
    // handlers/pages.go
    package handlers

    import (
        "html/template"
        "net/http"
        "github.com/buttercms/buttercms-go"
    )

    var templates = template.Must(template.ParseGlob("templates/*.html"))

    func LandingPageHandler(w http.ResponseWriter, r *http.Request) {
        slug := r.URL.Query().Get("slug")
        if slug == "" {
            slug = "home"
        }

        params := map[string]string{
            "locale": "en",
        }

        page, err := ButterCMS.GetPage("landing_page", slug, params)
        if err != nil {
            http.Error(w, "Page not found", http.StatusNotFound)
            return
        }

        templates.ExecuteTemplate(w, "landing.html", page)
    }
    ```

    ```go theme={null}
    // main.go
    func main() {
        http.HandleFunc("/", handlers.LandingPageHandler)
        http.HandleFunc("/page/", handlers.LandingPageHandler)
        http.ListenAndServe(":8080", nil)
    }
    ```
  </Tab>

  <Tab title="Gin Framework">
    ```go theme={null}
    // handlers/pages.go
    package handlers

    import (
        "net/http"
        "github.com/buttercms/buttercms-go"
        "github.com/gin-gonic/gin"
    )

    func LandingPage(c *gin.Context) {
        slug := c.Param("slug")
        if slug == "" {
            slug = "home"
        }

        params := map[string]string{
            "locale": "en",
        }

        page, err := ButterCMS.GetPage("landing_page", slug, params)
        if err != nil {
            c.HTML(http.StatusNotFound, "404.html", nil)
            return
        }

        c.HTML(http.StatusOK, "landing.html", page)
    }
    ```

    ```go theme={null}
    // main.go
    r.GET("/", handlers.LandingPage)
    r.GET("/page/:slug", handlers.LandingPage)
    ```
  </Tab>
</Tabs>

```html theme={null}
<!-- templates/landing.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{{.Data.Fields.headline}}</title>
</head>
<body>
    <main>
        <h1>{{.Data.Fields.headline}}</h1>
        <p>{{.Data.Fields.subheadline}}</p>
        {{if .Data.Fields.hero_image}}
        <img src="{{.Data.Fields.hero_image}}" alt="{{.Data.Fields.headline}}" />
        {{end}}
        {{.Data.Fields.body | safeHTML}}
    </main>
</body>
</html>
```

## Collections

<Accordion title="ButterCMS Dashboard Setup">
  **Create a Collection:**

  1. Go to **Content Types** → **Collections** → Click **+**
  2. Add fields to your schema:
     * **Short Text** field named `name`
     * **Media** field named `logo`
     * **WYSIWYG** field named `description`
  3. Click **Create Collection** and name it `Brands` (key: `brands`)

  **Add Collection Items:**

  1. After saving, the create item screen loads automatically
  2. Fill in your brand details and click **Save**
  3. Add more items via **Collections** → hover **Brands** → click **+**
</Accordion>

<Tabs>
  <Tab title="Standard Library">
    ```go theme={null}
    // handlers/brands.go
    func BrandsHandler(w http.ResponseWriter, r *http.Request) {
        content, err := ButterCMS.GetContentFields([]string{"brands"}, nil)
        if err != nil {
            http.Error(w, "Error fetching brands", http.StatusInternalServerError)
            return
        }

        templates.ExecuteTemplate(w, "brands.html", content)
    }
    ```
  </Tab>

  <Tab title="Gin Framework">
    ```go theme={null}
    // handlers/brands.go
    func Brands(c *gin.Context) {
        content, err := ButterCMS.GetContentFields([]string{"brands"}, nil)
        if err != nil {
            c.HTML(http.StatusInternalServerError, "error.html", nil)
            return
        }

        c.HTML(http.StatusOK, "brands.html", content)
    }
    ```
  </Tab>
</Tabs>

```html theme={null}
<!-- templates/brands.html -->
<!DOCTYPE html>
<html>
<body>
    <main>
        <h1>Our Brands</h1>
        <ul>
            {{range .Data.brands}}
            <li>
                <img src="{{.logo}}" alt="{{.name}}" />
                <h2>{{.name}}</h2>
                {{.description | safeHTML}}
            </li>
            {{end}}
        </ul>
    </main>
</body>
</html>
```

## Dynamic Components

<Accordion title="ButterCMS Dashboard Setup">
  **Create Components:**

  1. Go to **Content Types** → **Components** → Click **+**
  2. Create a **Hero** component with fields:
     * `headline` (Short Text)
     * `subheadline` (Long Text)
     * `image` (Media)
     * `button_label` (Short Text)
     * `button_url` (Short Text)
  3. Create additional components (Features, Testimonials, CTA, etc.)

  **Add Component Picker to Page Type:**

  1. Edit your Page Type schema
  2. Add a **Component** field named `body`
  3. Select which components can be used in this field
  4. Content editors can now drag-and-drop components when creating pages
</Accordion>

### Component Renderer

```go theme={null}
// components/renderer.go
package components

import (
    "bytes"
    "html/template"
)

var componentTemplates = template.Must(template.ParseGlob("templates/components/*.html"))

type Component struct {
    Type   string                 `json:"type"`
    Fields map[string]interface{} `json:"fields"`
}

func RenderComponents(components []Component) template.HTML {
    var buf bytes.Buffer

    for _, comp := range components {
        templateName := comp.Type + ".html"
        if err := componentTemplates.ExecuteTemplate(&buf, templateName, comp.Fields); err != nil {
            continue // Skip unknown components
        }
    }

    return template.HTML(buf.String())
}
```

### Component Template

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

### Using in Handlers

```go theme={null}
// handlers/component_page.go
func ComponentPageHandler(w http.ResponseWriter, r *http.Request) {
    slug := r.URL.Path[len("/landing/"):]

    page, err := ButterCMS.GetPage("component_page", slug, nil)
    if err != nil {
        http.Error(w, "Page not found", http.StatusNotFound)
        return
    }

    // Extract components from page body
    bodyComponents := page.Data.Fields["body"].([]interface{})
    var comps []components.Component
    for _, c := range bodyComponents {
        comp := c.(map[string]interface{})
        comps = append(comps, components.Component{
            Type:   comp["type"].(string),
            Fields: comp["fields"].(map[string]interface{}),
        })
    }

    data := map[string]interface{}{
        "page":       page,
        "components": components.RenderComponents(comps),
    }

    templates.ExecuteTemplate(w, "component-page.html", data)
}
```

## Blog

<Accordion title="ButterCMS Dashboard Setup">
  ButterCMS includes a pre-built blog engine with posts, authors, categories, and tags.

  **Create a Blog Post:**

  1. Go to **Blog Posts** → **New Post**
  2. Enter a title, body content using the rich text editor
  3. Optionally add a featured image, categories, tags, and author
  4. Click **Publish**

  The blog engine automatically provides:

  * Post slugs, summaries, and SEO fields
  * Author profiles with bio and social links
  * Categories and tags for organization
  * RSS and Atom feeds
</Accordion>

<Tabs>
  <Tab title="Blog Post List">
    <Tabs>
      <Tab title="Standard Library">
        ```go theme={null}
        // handlers/blog.go
        func BlogListHandler(w http.ResponseWriter, r *http.Request) {
            page := r.URL.Query().Get("page")
            if page == "" {
                page = "1"
            }

            params := map[string]string{
                "page":      page,
                "page_size": "10",
            }

            posts, err := ButterCMS.GetPosts(params)
            if err != nil {
                http.Error(w, "Error fetching posts", http.StatusInternalServerError)
                return
            }

            templates.ExecuteTemplate(w, "blog-list.html", posts)
        }
        ```
      </Tab>

      <Tab title="Gin Framework">
        ```go theme={null}
        // handlers/blog.go
        func BlogList(c *gin.Context) {
            params := map[string]string{
                "page":      c.DefaultQuery("page", "1"),
                "page_size": "10",
            }

            posts, err := ButterCMS.GetPosts(params)
            if err != nil {
                c.HTML(http.StatusInternalServerError, "error.html", nil)
                return
            }

            c.HTML(http.StatusOK, "blog-list.html", posts)
        }
        ```
      </Tab>
    </Tabs>

    ```html theme={null}
    <!-- templates/blog-list.html -->
    <!DOCTYPE html>
    <html>
    <body>
        <h1>Blog</h1>
        <ul>
            {{range .Data}}
            <li>
                <h2><a href="/blog/{{.Slug}}">{{.Title}}</a></h2>
                <p>{{.Summary}}</p>
                <span>By {{.Author.FirstName}} {{.Author.LastName}}</span>
            </li>
            {{end}}
        </ul>
        {{if .Meta.NextPage}}
        <a href="/blog?page={{.Meta.NextPage}}">Next Page</a>
        {{end}}
    </body>
    </html>
    ```
  </Tab>

  <Tab title="Single Blog Post">
    <Tabs>
      <Tab title="Standard Library">
        ```go theme={null}
        // handlers/blog.go
        func BlogPostHandler(w http.ResponseWriter, r *http.Request) {
            slug := r.URL.Path[len("/blog/"):]

            post, err := ButterCMS.GetPost(slug)
            if err != nil {
                http.Error(w, "Post not found", http.StatusNotFound)
                return
            }

            templates.ExecuteTemplate(w, "blog-post.html", post)
        }
        ```
      </Tab>

      <Tab title="Gin Framework">
        ```go theme={null}
        // handlers/blog.go
        func BlogPost(c *gin.Context) {
            slug := c.Param("slug")

            post, err := ButterCMS.GetPost(slug)
            if err != nil {
                c.HTML(http.StatusNotFound, "404.html", nil)
                return
            }

            c.HTML(http.StatusOK, "blog-post.html", post)
        }
        ```
      </Tab>
    </Tabs>

    ```html theme={null}
    <!-- templates/blog-post.html -->
    <!DOCTYPE html>
    <html>
    <body>
        <article>
            <h1>{{.Data.Title}}</h1>
            <p>By {{.Data.Author.FirstName}} {{.Data.Author.LastName}}</p>
            {{if .Data.FeaturedImage}}
            <img src="{{.Data.FeaturedImage}}" alt="{{.Data.Title}}" />
            {{end}}
            {{.Data.Body | safeHTML}}
            <a href="/blog">Back to Posts</a>
        </article>
    </body>
    </html>
    ```
  </Tab>
</Tabs>

## Caching

Use an in-memory cache for better performance:

```go theme={null}
// cache/cache.go
package cache

import (
    "sync"
    "time"
)

type CacheEntry struct {
    Data      interface{}
    ExpiresAt time.Time
}

type Cache struct {
    entries map[string]CacheEntry
    mu      sync.RWMutex
    ttl     time.Duration
}

func New(ttl time.Duration) *Cache {
    return &Cache{
        entries: make(map[string]CacheEntry),
        ttl:     ttl,
    }
}

func (c *Cache) Get(key string) (interface{}, bool) {
    c.mu.RLock()
    defer c.mu.RUnlock()

    entry, ok := c.entries[key]
    if !ok || time.Now().After(entry.ExpiresAt) {
        return nil, false
    }
    return entry.Data, true
}

func (c *Cache) Set(key string, data interface{}) {
    c.mu.Lock()
    defer c.mu.Unlock()

    c.entries[key] = CacheEntry{
        Data:      data,
        ExpiresAt: time.Now().Add(c.ttl),
    }
}

func (c *Cache) Invalidate(pattern string) {
    c.mu.Lock()
    defer c.mu.Unlock()

    for key := range c.entries {
        if strings.Contains(key, pattern) {
            delete(c.entries, key)
        }
    }
}
```

## SEO

```go theme={null}
// handlers/pages.go
type SEOData struct {
    Title       string
    Description string
    OGTitle     string
    OGImage     string
}

func extractSEO(page *ButterCMS.PageResponse) SEOData {
    fields := page.Data.Fields
    seo := fields["seo"].(map[string]interface{})

    return SEOData{
        Title:       getStringOr(seo, "title", fields["headline"].(string)),
        Description: getStringOr(seo, "description", ""),
        OGTitle:     getStringOr(seo, "og_title", getStringOr(seo, "title", "")),
        OGImage:     getStringOr(seo, "og_image", ""),
    }
}

func getStringOr(m map[string]interface{}, key, fallback string) string {
    if v, ok := m[key].(string); ok {
        return v
    }
    return fallback
}
```

## Resources

<CardGroup cols={2}>
  <Card title="Go Starter" icon="rocket" href="../starter-projects/go">
    Pre-configured starter project
  </Card>

  <Card title="Go SDK" icon="golang" href="../sdks/go-sdk">
    Complete SDK reference
  </Card>

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

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