Run this in your commandline:
npm install buttercms --save
Butter can also be loaded using a CDN:
<script src="https://cdnjs.buttercms.com/buttercms-1.1.4.min.js"></script>
Or if you're using Yarn:
yarn add buttercms
The source code to a complete sample Next.js application is available on Github.
To test the ButterCMS API, set your API token:
var butter = require('buttercms')('your_api_token');
Or if using ES6 syntax:
import Butter from 'buttercms';
const butter = Butter('your_api_token');
Then run:
butter.post.list({page: 1, page_size: 10}).then(function(response) {
console.log(response)
})
This API request fetches our blog posts. Your account comes with one example post which you'll see in the response. If you get a response it means you're now able to connect to our API.
Next, create a new directory for your app and add a package.json file:
{
"name": "react-blog"
}
Next, install Next.js, React, and the ButterCMS API. As of the time of this writing, we'll want to install the Next.js for custom routing we'll use later:
npm install next@beta react react-dom buttercms --save
Then add a script to your package.json:
{
"scripts": {
"start": "next"
}
}
Next.js treats every js file in the ./pages
directory as a page. Let's setup a basic homepage by creating a ./pages/index.js
inside your project:
export default () => (
<div>My blog homepage</div>
)
And then just run npm run start and go to http://localhost:3000
Then create a ./pages/post.js
and check it out at http://localhost:3000/post
export default () => (
<div>A blog post</div>
)
- Need help? Contact us via email or livechat.
- Found a bug? You can open a GitHub issue.
Contents
Headless CMS
ButterCMS is a headless CMS that lets you manage content using our dashboard and integrate it into your tech stack of choice with our content APIs. You can use ButterCMS for new projects as well as add it to existing codebases.
If you're familiar with Wordpress, see how ButterCMS compares to WordPress.
ButterCMS provides a user-friendly UI for managing marketing sites, blogging, and custom content scenarios. We can be used for SEO landing pages, customer case studies, company news & updates, events + webinar pages, education center, location pages, knowledgebases, and more.
We are different from a traditional CMS like Drupal or Wordpress in that we're not a large piece of software you need to download, host, customize, and maintain. Instead we provide easy to consume, performant content API's that you add to your application.
For example, if you wanted to enable a non-technical person to be able to add customer case study pages to your marketing site, you might create a Case Study Page Type to represent these pages. The non-technical person would be able to manage these pages from our dashboard and the JSON API output would look something like this:
{
"data": {
"slug": "acme-co-case-study",
"fields": {
"seo_title": "Acme Co Customer Case Study",
"seo_description": "Acme Co saved 200% on Anvil costs with ButterCMS",
"title": "Acme Co loves ButterCMS",
"body": "<p>We've been able to make anvils faster than ever before! - Chief Anvil Maker</p>"
}
}
}
Use Postman to experiement
Postman is a great tool for experiementing with our API. We wrote a post about it here. Once you've installed Postman, click this button to quickly add our end point Collection to your Postman.
Webhooks
Webhooks are a powerful feature that allow you to notify your internal systems whenever content in ButterCMS has changed. You can learn more about Webhooks in this blog post.
Reloading the Next.js app on adding a new page
We can make use of the ButterCMS webhooks to notify our application everytime a new page is added or modified, and reload the next.js application.
You can add a new webhook by going to the https://buttercms.com/webhooks/ page. There, you can provide the URL of your deployed application:
Suppose your server is deployed with the address http://abc.def/
, we can create a /webhook-receiver
route, so that the resulting webhook URL configured on ButterCMS would be http://abc.def/webhook-receiver
. We will get a POST request everytime there is a change, so lets create the corresponding route inside server.js
server.post('/webhook-receiver', (req, res) => {
console.log('a post has been modified')
app
.prepare()
.then(() => {
console.log('app refreshed')
res.end()
})
.catch((e) => {
res.status(500).end()
})
})
The route will call the app.prepare
function, which reloads the Next.js application.
Image Transformation
ButterCMS has integrated with a rich image transformation API called Filestack. This allows you to modify your uploaded images in dozens of ways. Everything from resizing, cropping, effects, filters, applying watermarks and more. Check out Filestack full documentation for more detail.
After you upload an image to ButterCMS, it's stored on our CDN. To create a thumbnail, here's an example:
Original URL = https://cdn.buttercms.com/zjypya5tRny63LqhHQrv
Thumbnail URL = https://fs.buttercms.com/resize=width:200,height:200/zjypya5tRny63LqhHQrv
Resizing is just one of the many different transformations you can do to your images. Refer to the Filestack docs for full details.
Localization
ButterCMS has full support for localization of your content. Locale names and keys are completely customizable and there's no limit to the number of locales you can have. View our API Reference to learn how to query by locale.
Table of Contents
Introduction
Quickly launch a new marketing site or add CMS-powered pages to your existing site using our Pages.
Create a Single Page
Adding a CMS-powered page to your app involves three easy steps:
If you need help after reading this, contact us via email or livechat.
Create the Page Structure
Create a new Page and define it's structure using our Page Builder. Let's create an example homepage.
Populate the Content
Then populate our new page with content. In the next step, we'll call the ButterCMS API to retrieve this content from our app.
Integrate into your application
With your homepage defined, the ButterCMS Pages API will return it in JSON format like this:
{
"data": {
"slug": "homepage",
"fields": {
"seo_title": "Anvils and Dynamite | Acme Co",
"headline": "Acme Co provides supplies to your favorite cartoon heroes.",
"hero_image": "https://cdn.buttercms.com/c8oSTGcwQDC5I58km5WV",
"call_to_action": "Buy Now",
"customer_logos": [
{
"logo_image": "https://cdn.buttercms.com/c8oSTGcwQDC5I58km5WV"
},
{
"logo_image": "https://cdn.buttercms.com/c8oSTGcwQDC5I58km5WV"
}
]
}
}
}
To integrate this into your app, update pages/index.js
:
import React from 'react'
import Butter from 'buttercms'
import Head from 'next/head'
const butter = Butter('your_api_token')
export default class extends React.Component {
static async getInitialProps () {
const resp = await butter.page.retrieve('*', 'homepage')
return resp.data
}
render () {
const product = this.props.data
const {
seo_title: seoTitle,
hero_image: heroImage,
call_to_action: callToAction,
customer_logos: customerLogos,
headline } = product.fields
return (
<div>
<Head>
<title>{seoTitle}</title>
</Head>
<img src={heroImage} />
<h1>{headline}</h1>
<button>{callToAction}</button>
// Loop through customer_logos
</div>
)
}
}
That's it! If you browse to your homepage you'll see your homepage populated with the content you created in Butter.
Create multiple pages using Page Types
Overview Video
Let's say you want to add a set of customer case study pages to your marketing site. They all have the same structure but the content is different. Page Types are perfect for this scenario and involves three easy steps:
If you need help after reading this, contact us via email or livechat.
Create the Page Type structure
Create a Page Type to represent your Customer Case Study pages:
After saving, return to the configuration page by clicking the gear icon:
Then click on Create Page Type and name it "Customer Case Study". This will allow us to reuse this field configuration across multiple customer case study pages:
Populate the Content
Then populate our new page with content. In the next step, we'll call the ButterCMS API to retrieve this content from our app.
Integrate into your application
With a case study defined, the ButterCMS Pages API will return it in JSON format like this:
{
"data": {
"slug": "acme-co",
"fields": {
"facebook_open_graph_title": "Acme Co loves ButterCMS",
"seo_title": "Acme Co Customer Case Study",
"headline": "Acme Co saved 200% on Anvil costs with ButterCMS",
"testimonial": "<p>We've been able to make anvils faster than ever before! - <em>Chief Anvil Maker</em></p>\r\n<p><img src=\"https://cdn.buttercms.com/NiA3IIP3Ssurz5eNJ15a\" alt=\"\" caption=\"false\" width=\"249\" height=\"249\" /></p>",
"customer_logo": "https://cdn.buttercms.com/c8oSTGcwQDC5I58km5WV",
}
}
}
To integrate this into your app, create pages/case-study.js
:
import React from 'react'
import Butter from 'buttercms'
import Head from 'next/head'
const butter = Butter('your_api_token')
export default class extends React.Component {
static async getInitialProps ({ query }) {
const resp = await butter.page.retrieve('customer_case_study', query.slug)
return resp.data
}
render () {
const product = this.props.data
const { seo_title: seoTitle, customer_logo: customerLogo, headline, testimonial } = product.fields
return (
<div>
<Head>
<title>{seoTitle}</title>
</Head>
<div>
<img src={customerLogo} />
</div>
<h1>{headline}</h1>
<div dangerouslySetInnerHTML={{ __html: testimonial }} />
</div>
)
}
}
Update the routes in your app to route to the specified components
Finally, we add the route for the page to server.js:
server.get('/case-studies/:slug', (req, res) => {
return app.render(req, res, '/case-study', { slug: req.params.slug })
})
Setup the Customers Page to list all our customers
Create a new file pages/case-studies.js
In this file, we should:
- Initialize the butterCMS library
- On the getInitialProps hook, fetch the list of case studies
- Return the response data as the component props
import React from 'react'
import Butter from 'buttercms'
const butter = Butter('your_api_token')
export default class extends React.Component {
static async getInitialProps ({ query }) {
const resp = await butter.page.list('customer_case_study')
return resp.data
}
render () {
return (
<div>
{this.props.data.map((caseStudy, key) => {
return (
<div key={key}>
<img src={caseStudy.fields.customer_logo} />
<a href={`/case-studies/${caseStudy.slug}`}>{caseStudy.fields.headline}</a>
</div>
)
})}
</div>
)
}
}
That's it! The page your created in Butter dashboard will immediately show in your app.
If you need help after reading this, contact us via email or livechat.
Table of Contents
Introduction
Learn how to make content on a static page editable using Butter and React. Butter also works with other client-side JavaScript frameworks like Angular and Vue.js.
Content Fields are global pieces of content that can be managed by your team. They can be used for content that spans multiple pages (header, footer) or platforms (desktop, mobile). Each content field has unique ID for querying via our API. Let's see how you can use them to power a knowledge base. Again Content Fields are great for content that can appear in multiple places so this knowledge base could appear on your website and mobile app. This example will focus on your website.
Two-step process:
- Setup custom content fields in Butter
- Integrate the fields into your application
If you need help after reading this, contact us via email or livechat.
Setup content fields
Let's suppose we want to add a CMS to a static FAQ page with a title and a list of questions with answers.
Making your content dynamic with Butter is a two-step process:
- Setup custom content fields in Butter
- Integrate the fields into your application
To setup custom content fields, first sign in to the Butter dashboard.
Create a new workspace or click on an existing one. Workspaces let you organize content fields in a friendly way for content editors and have no effect on development or the API. For example, a real-estate website might have a workspace called "Properties" and another called "About Page".
Once you're in a workspace click the button to create a new content field. Choose the "Object" type and name the field "FAQ Headline":
After saving, add another field but this time choose the "Collection" type and name the field FAQ Items
:
On the next screen setup two properties for items in the collection:
Now go back to your workspace and update your heading and FAQ items.
Integrate your app
To display this dynamic content in our app, we fetch the fields with an API call and then reference them in our view. Here's what the code looks like:
import React, { Component } from 'react'
import Butter from 'buttercms'
const butter = Butter('your_api_token')
class Faq extends Component {
static async getInitialProps () {
const resp = await butter.content.retrieve([ 'faq_headline', 'faq_items' ])
return resp.data.data
}
render () {
return (
<div>
<h1>{this.props.faq_headline}</h1>
<ul>
{this.props.faq_items.map((item) => {
return (
<li>
<h4>{item.question}</h4>
<p>{item.answer}</p>
</li>
)
})}
</ul>
</div>
)
}
}
export default Faq
That's it! The values entered in the Butter dashboard will immediately update the content in our app.
If you need help after reading this, contact us via email or livechat.
Table of Contents
- Introduction
- Displaying posts
- Categories, tags, and authors
- RSS, Atom, and Sitemap
- Comments
- Social Sharing
- CSS
- Migration
Introduction
Learn how to quickly build a CMS-powered blog with React. Butter also works with other client-side JavaScript frameworks like Angular and Vue.js. To get started even quicker, here's a set of sample blog templates you can use.
If you need help after reading this, contact us via email or livechat.
Display posts
To display posts we create a new component in blog.js
to fetch and list blog posts from the Butter API. See our API reference for additional options such as filtering by category or author. The response also includes some metadata we'll use for pagination.
In blog.js
:
import React from 'react'
import Link from 'next/link'
import Butter from 'buttercms'
const butter = Butter('your_api_token')
export default class extends React.Component {
static async getInitialProps({ query }) {
let page = query.page || 1;
const resp = await butter.post.list({page: page, page_size: 10})
return resp.data;
}
render() {
const { next_page, previous_page } = this.props.meta;
return (
<div>
{this.props.data.map((post) => {
return (
<div><a href={`/post/${post.slug}`}>{post.title}</a></div>
)
})}
<div>
{previous_page && <Link href={`/?page=${previous_page}`}><a>Prev</a></Link>}
{next_page && <Link href={`/?page=${next_page}`}><a>Next</a></Link>}
</div>
</div>
)
}
}
With Next.js getInitialProps
will execute on the server on initial page loads, and then on the client when navigating to a different routes using the built-in <Link>
component. getInitialProps
also receives a context object with various properties – we access the query
property for handling pagination. We are fetching posts from a ButterCMS test account – sign in with Github to setup your own posts.
In our render()
method we use some clever syntax to only display pagination links only when they're applicable. Our post links will take us to a 404 – we'll get these working next.
Setup the Blog Post page to list a single post
We'll also update our post component to fetch blog posts via slug and render the title and body. See a full list of available post properties in our API reference:
import React from 'react'
import Butter from 'buttercms'
const butter = Butter('your_api_token')
export default class extends React.Component {
static async getInitialProps({ query }) {
const resp = await butter.post.retrieve(query.slug);
return resp.data;
}
render() {
const post = this.props.data;
return (
<div>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.body }} />
</div>
)
}
}
Add routes to the server
To get our post links working we need to setup dynamic routing for our blog posts. First, create a custom server ./server.js
that routes all /posts/:slug
URLs to our post component, and the /posts
URL to our index page:
const next = require('next')
const express = require('express')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
const port = 3000
app.prepare().then(() => {
const server = express()
server.get('/posts', (req, res) => {
return app.render(req, res, '/index', { slug: req.params.slug })
})
server.get('/posts/:slug', (req, res) => {
return app.render(req, res, '/post', { slug: req.params.slug })
})
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen(port, (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
Finally, update our package.json
start script to use our customer server and restart:
"scripts": {
"start": "node server.js"
}
Categories, Tags, and Authors
Use Butter's APIs for categories, tags, and authors to feature and filter content on your blog.
See our API reference for more information about these objects:
We can use the ButterCMS API to get all post categories and all posts under a category. Let's create routes in the server.js
file for the page categories, and the index of pages under a single category:
//...
// Page categories
server.get('/posts/categories', (req, res) => {
return app.render(req, res, '/categories')
})
// All pages under a single catrogory
server.get('/posts/category/:slug', (req, res) => {
return app.render(req, res, '/category', { slug: req.params.slug })
})
//...
We can then create the React component for the pages. Create a new file pages/categories.js
:
import React from 'react'
import Butter from 'buttercms'
const butter = Butter('your_api_token')
export default class extends React.Component {
static async getInitialProps ({ query }) {
const resp = await butter.category.list()
console.log(resp.data)
return resp.data
}
render () {
return (
<div>
{this.props.data.map((category, key) => {
return (
<div key={key}>
<a href={`/posts/category/${category.slug}`}>{category.name}</a>
</div>
)
})}
</div>
)
}
}
This will create a page that will list all categories, along with linking each of them to their own page. Now, create a pages/category.js
file, that will contain the component to list all pages under a single category:
import React from 'react'
import Butter from 'buttercms'
import Head from 'next/head'
const butter = Butter('your_api_token')
export default class extends React.Component {
static async getInitialProps ({ query }) {
const resp = await butter.category.retrieve(query.slug, {
include: 'recent_posts'
})
return resp.data
}
render () {
const category = this.props.data
return (
<div>
<Head>
<title>{category.name}</title>
</Head>
<h1>{category.name}</h1>
<div>
{this.props.data.recent_posts.map((post, key) => {
return (
<div key={key}>
<a href={`/posts/${post.slug}`}>{post.title}</a>
</div>
)
})}
</div>
</div>
)
}
}
The links on these pages will be to the individual posts themselves.
RSS, Atom, and Sitemap
Butter generates RSS, Atom, and sitemap XML markup. To use these on your blog, return the generated XML from the Butter API with the proper content type headers.
We can fetch the RSS, Atom, and Sitemap feeds using the ButterCMS API, and pipe the response to routes on our server. Create the /sitemap
, /rss
, and /atom
routes inside server.js
server.get('/sitemap', (req, res) => {
butter.feed.retrieve('sitemap').then((s) => {
res.send(s.data.data)
})
})
server.get('/atom', (req, res) => {
butter.feed.retrieve('atom').then((s) => {
res.send(s.data.data)
})
})
server.get('/rss', (req, res) => {
butter.feed.retrieve('rss').then((s) => {
res.send(s.data.data)
})
})
These routes will also update dynamically, everytime new content is added, or updated.
SEO
Next.js provides a <Head> component for setting HTML titles and meta tags. Add import Head from 'next/head'
to the top of ./pages/post.js
and use the component in the render()
method:
render() {
const post = this.props.data;
return (
<div>
<Head>
<title>{post.seo_title}</title>
<meta name="description" content={post.meta_description} />
<meta name="og:image" content={post.featured_image} />
</Head>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{__html: post.body }} />
</div>
)
}
Restart the server and inspect the HTML source of a post to verify that tags are getting set correctly.
Comments
Butter doesn't provide an API for comments due to the excellent existing options that integrate easily. Two popular services we recommend are:
Both products are free, include moderation capabilities, and give your audience a familiar commenting experience. They can also provide some additional distribution for your content since users in their networks can see when people comment on your posts. For a minimalist alternative to Disqus, check out RemarkBox or for an open-source option, Isso.
Social Sharing
To maximize sharing of your content, we recommend using a free tool called AddThis.
They provide an attractive and easy to integrate social sharing widget that you can add to your website.
CSS
Butter integrates into your front-end so you have complete control over the design of your blog. Use the following CSS as boilerplate for post content styling.
.post-container {
h1, h2, h3, h4, h5 {
font-weight: 600;
margin-bottom: 1em;
margin-top: 1.5em;
}
ul, ol {
margin-bottom: 1.25em;
li {
margin-bottom: 0.25em;
}
}
p {
font-family: Georgia, Cambria, "Times New Roman", Times, serif;
font-size: 1.25em;
line-height: 1.58;
margin-bottom: 1.25em;
font-weight: 400;
letter-spacing: -.003em;
}
/* Responsive default image width */
img {
max-width: 100%;
height: auto;
}
/* Responsive floating */
@media only screen and (min-width: 720px) {
.butter-float-left {
float: left;
margin: 0px 10px 10px 0px;
}
.butter-float-right {
float: right;
margin: 0px 0px 10px 10px;
}
}
/* Image caption */
figcaption {
font-style: italic;
text-align: center;
color: #ccc;
}
p code {
padding: 2px 4px;
font-size: 90%;
color: #c7254e;
background-color: #f9f2f4;
border-radius: 4px;
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
}
pre {
display: block;
padding: 1em;
margin: 0 0 2em;
font-size: 1em;
line-height: 1.4;
word-break: break-all;
word-wrap: break-word;
color: #333333;
background-color: #f5f5f5;
font-family: Menlo, Monaco,Consolas, "Courier New", monospace;
}
}
Migration
To import content from another platform like WordPress or Medium, send us an email.