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.

Installation

npm install buttercms
Add your API token to .env:
BUTTER_CMS_API_KEY=your_api_token

Initialize the client

Create a reusable client instance:
// src/lib/buttercms.ts
import Butter from 'buttercms';
import { BUTTER_CMS_API_KEY } from '$env/static/private';

const butter = Butter(BUTTER_CMS_API_KEY);

export default butter;
For complete SDK documentation including all available methods and configuration options, see the JavaScript SDK Reference.

Pages

// src/routes/[slug]/+page.server.ts
import butter from '$lib/buttercms';
import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ params }) => {
  try {
    const response = await butter.page.retrieve('landing-page', params.slug);
    return { page: response.data.data };
  } catch {
    throw error(404, 'Page not found');
  }
};
<!-- src/routes/[slug]/+page.svelte -->
<script lang="ts">
  import type { PageData } from './$types';

  export let data: PageData;
</script>

<main>
  <h1>{data.page.fields.headline}</h1>
  <p>{data.page.fields.subheadline}</p>
  {#if data.page.fields.hero_image}
    <img src={data.page.fields.hero_image} alt={data.page.fields.headline} />
  {/if}
  {@html data.page.fields.body}
</main>

Collections

// src/routes/brands/+page.server.ts
import butter from '$lib/buttercms';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async () => {
  const response = await butter.content.retrieve(['brands']);
  return { brands: response.data.data.brands };
};
<!-- src/routes/brands/+page.svelte -->
<script lang="ts">
  import type { PageData } from './$types';

  export let data: PageData;
</script>

<main>
  <h1>Our Brands</h1>
  <ul>
    {#each data.brands as brand}
      <li>
        <img src={brand.logo} alt={brand.name} />
        <h2>{brand.name}</h2>
        {@html brand.description}
      </li>
    {/each}
  </ul>
</main>

Dynamic components

Component Renderer

<!-- src/lib/components/ComponentRenderer.svelte -->
<script lang="ts">
  import Hero from './Hero.svelte';
  import Features from './Features.svelte';
  import Testimonials from './Testimonials.svelte';
  import CTA from './CTA.svelte';
  import type { ComponentType } from 'svelte';

  interface ButterComponent {
    type: string;
    fields: Record<string, any>;
  }

  export let components: ButterComponent[];

  const componentMap: Record<string, ComponentType> = {
    hero: Hero,
    features: Features,
    testimonials: Testimonials,
    cta: CTA,
  };
</script>

{#each components as component}
  {#if componentMap[component.type]}
    <svelte:component this={componentMap[component.type]} {...component.fields} />
  {/if}
{/each}

Example Component

<!-- src/lib/components/Hero.svelte -->
<script lang="ts">
  export let headline: string;
  export let subheadline: string;
  export let image: string;
  export let button_label: string;
  export let button_url: string;
</script>

<section class="hero">
  <h1>{headline}</h1>
  <p>{subheadline}</p>
  {#if button_label}
    <a href={button_url}>{button_label}</a>
  {/if}
  {#if image}
    <img src={image} alt={headline} />
  {/if}
</section>

Using in Pages

<!-- src/routes/landing/[slug]/+page.svelte -->
<script lang="ts">
  import type { PageData } from './$types';
  import ComponentRenderer from '$lib/components/ComponentRenderer.svelte';

  export let data: PageData;
</script>

<main>
  <ComponentRenderer components={data.page.fields.body} />
</main>

Blog

// src/routes/blog/+page.server.ts
import butter from '$lib/buttercms';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ url }) => {
  const page = parseInt(url.searchParams.get('page') || '1');
  const response = await butter.post.list({ page, page_size: 10 });
  return {
    posts: response.data.data,
    meta: response.data.meta,
  };
};
<!-- src/routes/blog/+page.svelte -->
<script lang="ts">
  import type { PageData } from './$types';

  export let data: PageData;
</script>

<main>
  <h1>Blog</h1>
  <ul>
    {#each data.posts as post}
      <li>
        <h2><a href="https://buttercms.com/blog/{post.slug}">{post.title}</a></h2>
        {@html post.summary}
        <span>By {post.author.first_name} {post.author.last_name}</span>
      </li>
    {/each}
  </ul>
  {#if data.meta.next_page}
    <a href="/blog?page={data.meta.next_page}">Next Page</a>
  {/if}
</main>

Preview Mode

Enable draft content preview:
// src/routes/preview/[...path]/+page.server.ts
import Butter from 'buttercms';
import { BUTTER_CMS_API_KEY } from '$env/static/private';
import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ params, url }) => {
  const secret = url.searchParams.get('secret');

  if (secret !== process.env.PREVIEW_SECRET) {
    throw error(401, 'Invalid preview token');
  }

  const butter = Butter(BUTTER_CMS_API_KEY, true); // Enable preview mode

  const [pageType, slug] = params.path.split('/');

  try {
    const response = await butter.page.retrieve(pageType, slug);
    return { page: response.data.data, isPreview: true };
  } catch {
    throw error(404, 'Page not found');
  }
};

SEO

<!-- src/routes/[slug]/+page.svelte -->
<script lang="ts">
  import type { PageData } from './$types';
  import { page } from '$app/stores';

  export let data: PageData;

  $: seo = data.page.fields.seo || {};
</script>

<svelte:head>
  <title>{seo.title || data.page.fields.headline}</title>
  {#if seo.description}
    <meta name="description" content={seo.description} />
  {/if}
  <meta property="og:title" content={seo.og_title || seo.title || data.page.fields.headline} />
  {#if seo.og_description || seo.description}
    <meta property="og:description" content={seo.og_description || seo.description} />
  {/if}
  {#if seo.og_image}
    <meta property="og:image" content={seo.og_image} />
  {/if}
</svelte:head>

<main>
  <h1>{data.page.fields.headline}</h1>
  {@html data.page.fields.body}
</main>

Resources

JavaScript SDK

Complete SDK reference

Content API

REST API documentation