How to build a headless website with ButterCMS and NextJS
What is a headless website?
There is a trend in ecommerce and marketing websites to go "headless". The first natural question is: “What is Headless?”, especially in relation to e-commerce. Headless in the context of e-commerce means to separate the backend (content) from the frontend (code). By separating the backend and frontend, the two can now stand as separate entities, allowing content to be cached and statically served. Now that content is static, meaning it's not going to change, the code does not have to repeatedly look for it, directly resulting in an increase in site performance and page speed.
In a monolithic or traditional website build, if the content is mistakenly changed, that issue will update on the live site as well. This is a security vulnerability and quality assurance problem that decoupling the frontend from the backend prevents. Once it's seen why headless website builds are better not only for performance but security as well, the next question is asked: how to build a headless site?
ButterCMS + NextJS
There are a few options to pick from when it comes to a Headless CMS. A headless site is achievable using CraftQL with CraftCMS, and other web software that support this, but for ease of use above all, we recommend ButterCMS. In a previous post we have explained why ButterCMS is king, and we still stand behind that. A ButterCMS and static site generator website can completely revitalize page performance and add another layer of security. This combined tech stack is a great way to benefit from a headless CMS, with a fast-loading frontend design. With custom components that can handle any data structure for copy and content, their easy-to-use API can be quickly connected with a React frontend. In this demo we’ll choose one of the most popular static site generators, Nextjs to pair with ButterCMS. One of the reasons we choose Nextjs aside from its popularity is its great documentation, and ease of use.
The first phase of development after creating an account on ButterCMS (which pre-fills your account with starter data for testing) is to create a codebase to render the data. The first step for this since we are using Next.js is to run npx create-next-app@latest . This creates a Next.js static site for you on https://localhost:3000 after running npm run dev, and gives a file structure for pages that we will be using.
Connect To ButterCMS
Now that we have a page rendering on a localhost, the next step is to connect to the Butter API. The most secure way to do this is to use environment variables, so to do this we create a .env file to hold the butter API key:
NEXT_PUBLIC_BUTTER_CMS_API_KEY=paste_key_here
Then update the next.config.js file so that it can process environment variables (and allow images from the butter CDN as a valid source) like this:
/** @type {import('next').NextConfig} */ const nextConfig = {
reactStrictMode: true, env: {
BASE_URL: process.env.BASE_URL, },
images: { domains: ['cdn.buttercms.com'],
}, }
module.exports = nextConfig
Fetch ButterCMS Data
Now that we have the API key safely secured at the code level, we can start to fetch content from the butterCMS. Create a file lib/api.js, which will be the file that holds the functions that fetch data.
import Butter from 'buttercms' let butter
const postsPageSize = 1 try {
butter = Butter(process.env.NEXT_PUBLIC_BUTTER_CMS_API_KEY) } catch (e) {
console.log(e) }
export async function getCategories() { try {
const response = await butter.category.list() return response?.data?.data
} catch (e) { throw e.response.data.detail
} }
export async function getPostsData({ page, pageSize, tag, category } = { page: 1, pageSize: defaultPostCount },) { try {
const params = { page_size: pageSize || defaultPostCount,
page: page || 1, }
if (tag) { params.tag_slug = tag
} if (category) {
params.category_slug = category }
const response = await butter.post.list(params) return {
posts: response?.data?.data, prevPage: response?.data?.meta.previous_page,
nextPage: response?.data?.meta.next_page, }
} catch (e) { throw e.response.data.detail
} }
With these functions to retrieve blog data, which is populated by our starter account, we can start pulling it into our code so we can display it on a page. Since we want this data statically, meaning that the code only fetches it at build time, we want to use Next.js’s getStaticProps() function. Inside it we can reference and call the functions we defined above in our lib/api.js like so:
export async function getStaticProps() { try {
const blogPosts = (await getPostsData()).posts const categories = await getCategories()
return { props: { posts: blogPosts, categories } } } catch (e) {
console.log('Could not get posts', e) return {
props: { posts: [], categories: [] }, }
} return { props: { posts: [], categories: [] } }
} Render Data
With this function, it is now time to start rendering the data! We want to pass { posts, categories } as our props to the Home component in this same file, so we can map through them. ButterCMS utilizes data collections to allow for any type of information. In this example, we are demonstrating the use of their built-in blog content, but this same code structure can be used for products, custom pages, promotions, and more.
Altogether your code should look like this:
import { getPostsData, getCategories,} from '../lib/api' export default function Home({ posts, categories }) {
console.log('posts, categories', posts, categories) return (
<section> <div>
{posts?.map((post, idx)=> { return (<p key={idx}>{post.title}</p>)
})} </div>
<div> {categories?.map((category, idx)=> {
return (<p key={idx}>{category.name}</p>) })}
</div> </section>
) }
export async function getStaticProps() { try {
const blogPosts = (await getPostsData()).posts const categories = await getCategories()
return { props: { posts: blogPosts, categories } } } catch (e) {
console.log('Could not get posts', e) return {
props: { posts: [], categories: [] }, }
} return { props: { posts: [], categories: [] } }
}
Conclusion
That’s it! That's all that's needed to statically fetch and render blog data from ButterCMS! The setup is not too complicated and only requires a few steps in order to connect. After this, the possibilities are endless for what can be built, while also building lightning-fast pages! Reference these ButterCMS and Nextjs Headless builds here for more examples on how to connect and deploy to Vercel:
https://github.com/ButterCMS/react-cms-blog-with-next-js
https://github.com/vercel/next.js/tree/canary/examples/cms-buttercms
If you need React development, modern designs, or a ButterCMS partner you can rely on, look no further than Electric Enjin. We build custom solutions to help businesses integrate their existing content management systems, product data, customer base, and more. ButterCMS and Next.js is a great way to get started building a platform that provides a strong foundation for all of your current and future business needs. A headless website can offer extreme speed and performance improvements, which frequently results in better conversion rates. Contact us today for all design, development, and CRO services.
Looking for help with an RFP Website redesign? Contact us today to get started.