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.
Nuxt Starter Project Hit the ground running with a pre-configured Nuxt + ButterCMS setup.
Installation
Add your API token to .env:
NUXT_PUBLIC_BUTTER_CMS_API_KEY = your_api_token
Initialize the client
Create a composable for the ButterCMS client:
// composables/useButter.ts
import Butter from 'buttercms' ;
export function useButter () {
const config = useRuntimeConfig ();
return Butter ( config . public . butterCmsApiKey );
}
Update nuxt.config.ts:
// nuxt.config.ts
export default defineNuxtConfig ({
runtimeConfig: {
public: {
butterCmsApiKey: process . env . NUXT_PUBLIC_BUTTER_CMS_API_KEY ,
},
} ,
}) ;
For complete SDK documentation including all available methods and configuration options, see the JavaScript SDK Reference .
Pages
<!-- pages/[slug].vue -->
< script setup lang = "ts" >
const route = useRoute ();
const butter = useButter ();
interface PageFields {
headline : string ;
subheadline : string ;
hero_image : string ;
body : string ;
}
const { data : page , error } = await useAsyncData (
`page- ${ route . params . slug } ` ,
async () => {
const response = await butter . page . retrieve ( 'landing-page' , route . params . slug as string );
return response . data . data as { fields : PageFields };
}
);
if ( error . value ) {
throw createError ({ statusCode: 404 , message: 'Page not found' });
}
</ script >
< template >
< main v-if = " page " >
< h1 > {{ page . fields . headline }} </ h1 >
< p > {{ page . fields . subheadline }} </ p >
< img v-if = " page . fields . hero_image " : src = " page . fields . hero_image " : alt = " page . fields . headline " />
< div v-html = " page . fields . body " / >
</ main >
</ template >
<!-- pages/_slug.vue -->
< template >
< main v-if = " page " >
< h1 > {{ page . fields . headline }} </ h1 >
< p > {{ page . fields . subheadline }} </ p >
< img v-if = " page . fields . hero_image " : src = " page . fields . hero_image " : alt = " page . fields . headline " />
< div v-html = " page . fields . body " / >
</ main >
</ template >
< script >
import Butter from 'buttercms' ;
const butter = Butter ( process . env . BUTTER_CMS_API_KEY );
export default {
async asyncData ({ params , error }) {
try {
const response = await butter . page . retrieve ( 'landing-page' , params . slug );
return { page: response . data . data };
} catch ( err ) {
error ({ statusCode: 404 , message: 'Page not found' });
}
} ,
} ;
</ script >
Collections
<!-- pages/brands.vue -->
< script setup lang = "ts" >
const butter = useButter ();
interface Brand {
name : string ;
logo : string ;
description : string ;
}
const { data : brands } = await useAsyncData ( 'brands' , async () => {
const response = await butter . content . retrieve ([ 'brands' ]);
return response . data . data . brands as Brand [];
});
</ script >
< template >
< main >
< h1 > Our Brands </ h1 >
< ul >
< li v-for = " ( brand , index ) in brands " : key = " index " >
< img : src = " brand . logo " : alt = " brand . name " />
< h2 > {{ brand . name }} </ h2 >
< div v-html = " brand . description " / >
</ li >
</ ul >
</ main >
</ template >
<!-- pages/brands.vue -->
< template >
< main >
< h1 > Our Brands </ h1 >
< ul >
< li v-for = " ( brand , index ) in brands " : key = " index " >
< img : src = " brand . logo " : alt = " brand . name " />
< h2 > {{ brand . name }} </ h2 >
< div v-html = " brand . description " / >
</ li >
</ ul >
</ main >
</ template >
< script >
import Butter from 'buttercms' ;
const butter = Butter ( process . env . BUTTER_CMS_API_KEY );
export default {
async asyncData () {
const response = await butter . content . retrieve ([ 'brands' ]);
return { brands: response . data . data . brands };
} ,
} ;
</ script >
Dynamic components
Component Renderer
<!-- components/ComponentRenderer.vue -->
< script setup lang = "ts" >
import { defineAsyncComponent , type Component as VueComponent } from 'vue' ;
interface ButterComponent {
type : string ;
fields : Record < string , any >;
}
defineProps <{
components : ButterComponent [];
}>();
const componentMap : Record < string , VueComponent > = {
hero: defineAsyncComponent (() => import ( './Hero.vue' )),
features: defineAsyncComponent (() => import ( './Features.vue' )),
testimonials: defineAsyncComponent (() => import ( './Testimonials.vue' )),
cta: defineAsyncComponent (() => import ( './CTA.vue' )),
};
</ script >
< template >
< template v-for = " ( component , index ) in components " : key = " index " >
< component
v-if = " componentMap [ component . type ] "
: is = " componentMap [ component . type ] "
v-bind = " component . fields "
/>
</ template >
</ template >
Example Component
<!-- components/Hero.vue -->
< script setup lang = "ts" >
defineProps <{
headline : string ;
subheadline : string ;
image : string ;
button_label : string ;
button_url : string ;
}>();
</ script >
< template >
< section class = "hero" >
< h1 > {{ headline }} </ h1 >
< p > {{ subheadline }} </ p >
< a v-if = " button_label " : href = " button_url " > {{ button_label }} </ a >
< img v-if = " image " : src = " image " : alt = " headline " />
</ section >
</ template >
Using in Pages
<!-- pages/landing/[slug].vue -->
< script setup lang = "ts" >
const route = useRoute ();
const butter = useButter ();
const { data : page , error } = await useAsyncData (
`landing- ${ route . params . slug } ` ,
async () => {
const response = await butter . page . retrieve ( 'landing-page' , route . params . slug as string );
return response . data . data ;
}
);
if ( error . value ) {
throw createError ({ statusCode: 404 , message: 'Page not found' });
}
</ script >
< template >
< main v-if = " page " >
< ComponentRenderer : components = " page . fields . body " />
</ main >
</ template >
Blog
Blog Post List
Single Blog Post
<!-- pages/blog/index.vue -->
< script setup lang = "ts" >
const route = useRoute ();
const butter = useButter ();
interface Post {
slug : string ;
title : string ;
summary : string ;
author : { first_name : string ; last_name : string };
}
const page = parseInt ( route . query . page as string ) || 1 ;
const { data } = await useAsyncData ( `blog-page- ${ page } ` , async () => {
const response = await butter . post . list ({ page , page_size: 10 });
return {
posts: response . data . data as Post [],
meta: response . data . meta ,
};
});
</ script >
< template >
< main v-if = " data " >
< h1 > Blog </ h1 >
< ul >
< li v-for = " post in data . posts " : key = " post . slug " >
< h2 >< NuxtLink : to = " `/blog/ ${ post . slug } ` " > {{ post . title }} </ NuxtLink ></ h2 >
< p v-html = " post . summary " / >
< span > By {{ post . author . first_name }} {{ post . author . last_name }} </ span >
</ li >
</ ul >
< NuxtLink v-if = " data . meta . next_page " : to = " `/blog?page= ${ data . meta . next_page } ` " >
Next Page
</ NuxtLink >
</ main >
</ template >
<!-- pages/blog/[slug].vue -->
< script setup lang = "ts" >
const route = useRoute ();
const butter = useButter ();
interface Post {
title : string ;
body : string ;
published : string ;
featured_image : string ;
author : { first_name : string ; last_name : string };
}
const { data : post , error } = await useAsyncData (
`post- ${ route . params . slug } ` ,
async () => {
const response = await butter . post . retrieve ( route . params . slug as string );
return response . data . data as Post ;
}
);
if ( error . value ) {
throw createError ({ statusCode: 404 , message: 'Post not found' });
}
</ script >
< template >
< article v-if = " post " >
< h1 > {{ post . title }} </ h1 >
< p >
By {{ post . author . first_name }} {{ post . author . last_name }} on
{{ new Date ( post . published ). toLocaleDateString () }}
</ p >
< img v-if = " post . featured_image " : src = " post . featured_image " : alt = " post . title " />
< div v-html = " post . body " / >
< NuxtLink to = "/blog" > Back to Posts </ NuxtLink >
</ article >
</ template >
Preview Mode
Enable content editors to preview draft content:
// composables/useButter.ts
import Butter from 'buttercms' ;
export function useButter () {
const config = useRuntimeConfig ();
const route = useRoute ();
const isPreview = route . query . preview === 'true' ;
return Butter ( config . public . butterCmsApiKey , isPreview );
}
SEO
<!-- pages/[slug].vue -->
< script setup lang = "ts" >
const route = useRoute ();
const butter = useButter ();
const { data : page } = await useAsyncData (
`page- ${ route . params . slug } ` ,
async () => {
const response = await butter . page . retrieve ( 'landing-page' , route . params . slug as string );
return response . data . data ;
}
);
const seo = computed (() => page . value ?. fields . seo || {});
useHead ({
title : () => seo . value . title || page . value ?. fields . headline ,
meta: [
{ name: 'description' , content : () => seo . value . description },
{ property: 'og:title' , content : () => seo . value . og_title || seo . value . title },
{ property: 'og:description' , content : () => seo . value . og_description || seo . value . description },
{ property: 'og:image' , content : () => seo . value . og_image },
],
});
</ script >
Resources
Nuxt Starter Pre-configured starter project
JavaScript SDK Complete SDK reference
GitHub Repository View source code
Content API REST API documentation