GSD

How to Build a Blog with React and ButterCMS

Posted by Chisom Uma on October 18, 2023

If you are a software developer, the odds that you have used React.js to build a web application are pretty high. This is because React is unarguably one of the most popular JavaScript frameworks in the software development world. React.js is a JavaScript front-end framework developed by the social media company Facebook (now Meta) and has been used to build other popular applications and web applications such as Instagram, SoundCloud Pulse, Dropbox, Gyroscope, Netflix, etc. 

React is known as a JavaScript-based UI development library. React aims to allow developers to create a much faster user interface for websites and applications easily. React uses a Virtual DOM, which is a copy of the real Document Object Module (DOM). Every time a web application is modified, the entire virtual DOM is updated first in order to determine the differences between the real DOM and the virtual DOM. When it discovers the differences, the DOM just updates the portion that has recently changed, leaving the rest unchanged.

According to Statistica, React is the second most used web framework among developers worldwide after Node.js, having a 42.62% usage rating from respondents. This implies that many blogs are built using the React framework. 

Let us look at some of the reasons why React is great for building a blog:

Why is React great for building blogs

SEO-friendly

React is a great framework for creating blogs because it is SEO-friendly. SEO (search engine optimization) is a very important and critical factor to consider if you are building a blog. Most websites or mobile applications use SEO to improve their presence online and increase the numbers of visitors they get. Of course, if you run a blog, or plan to run a blog, you want regular visitors to your site. 

React can be made more SEO-friendly by preventing Google from using JavaScript to render the content. This can be done with server-side rendering (SSR).

React is typically launched in your web browser following the download of all necessary files from the page you are browsing (client-side rendering). With SSR, you run the JavaScript code on the server before the user receives the files.

That can make the page a little bit faster (especially on older machines), but more crucially, it means that Google doesn't need to run JavaScript in order to display the content to the user. That way, they can view it right away without having to wait for React to load first.

Helps build a better user interface

React is an awesome framework for building a blog because it also helps in creating a better UI (user interface) for your blog. If you are going to build or run a blog, you ideally want your users to be able to interact easily with the interface of your React blog. A poorly built UI will eventually lead to the blog losing authenticity and, consequently, losing visitors. One of the best ways React helps in creating a better UI is by being declarative. According to the official React docs:

“React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.

Declarative views make your code more predictable and easier to debug.”

Reusable components

One of the key features of React is its components. The awesome thing about React components is that they can be reusable. Being able to use a component in multiple places saves time and money, especially if you are building a large-scale blog.

React is flexible

One of the awesome things about React is its flexibility. React can be used on different platforms to build high-quality user interfaces. React can be used to generate a static site like Gatsby. You can also use React for mobile app development using React Native.

Tutorial goals

In this article, readers will learn how they can set up a blog web application with React and manage the blog’s content using ButterCMS. This blog will have the following features:

  • Fetching data from ButterCMS to be displayed in the application.
  • An on-site search feature to easily sort content based on its category.

Tutorial prerequisites

To follow along with this article, the reader should meet the following criteria:

  • Have basic knowledge of the React.js framework and CSS
  • Have a version of Node.js installed on the local machine
  • Have basic knowledge of the CLI and its commands

Tools you need to build with React

Let’s look at some of the tools we need with React for our tutorial:

  • VS Code: Debugging, task execution, and version control are supported by the efficient code editor Visual Studio Code. It seeks to give developers only the resources they require for a quick code-build-debug cycle and leaves more sophisticated workflows to IDEs with more features, like Visual Studio IDE. We used VS Code in the development of our React blog, but you can use any code editor preferred.
  • Node and React installed on a local machine: Node.js should be installed on your local machine to enable you to successfully run and install packages like the React application on your device without issues.

Tutorial: Building a blog with React

There are different approaches to building a blog site with React. To build one, a developer may choose to hard-code a blog (i.e., manually entering all data to be displayed on the blog). While this could work out, it would be cumbersome to update the blog content, as this would always have to be done in the code. Moreover, it is not suitable for a developer-to-client project, as it would require the client to possess the technical know-how of the programming language used by the developer, or the developer’s assistance for the client to update the blog.

A better way to build blogs is to build a back-end to manage the site’s content separate from the blog. This would provide more flexibility in data management and handling, making it easier to edit and update the content of the blog. Also, with the addition of a back-end, CRUD operations can be made feasible for the client. The developer may use a database and create a separate authenticated route for the user to be able to gain the administrative privilege to update the blog. A much less complicated way to manage the back-end would be to use a content management system (CMS), namely a headless CMS. This approach provides users with a visual dashboard where they can view and manage all content available on the blog, and is much more suitable for those with little or no code experience.

We will start by building the front-end of our application with React, then we will set up our back-end content structure using ButterCMS, and, finally, we will link the CMS to our React application to display and manage content.

Building the blog’s front-end

The first step to setting up the front-end with React is to create a new React application. This can be done via the CLI in your chosen directory with the following command:

npx create-react-app blogreact

The above command creates a project folder blogreact with all necessary dependencies for React installed. 

To handle API requests and routing in our blog application and to navigate from the landing page to specific blog content, we will be using Axios and React-routers (a React module for creating dynamic routes in a React application). We can install these dependencies via the CLI with the following commands:

cd blogreact
// to move into the newly created react application
npm install react-router-dom axios

Once installation is complete, we can open this in a code editor and proceed to create the front-end of our application.

Defining the application’s routes

Firstly, we will define the routes for our blog application. These routes are the landing page, a route to a specific blog post, and a redirect route back to the home page.

We will do this using the React-router-dom package we installed. In index.js, make the following changes to allow for routing in the application:

import { BrowserRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

Then we can create specific routes in App.js:

import "./App.css";
import { Route, Routes } from "react-router-dom";
import HomePage from "./pages/HomePage";
import BlogPost from "./pages/BlogPost";
import { React, useState } from "react";

function App() {
const [getBlogContent, setGetBlogContent] = useState([]);
  const getData = (blog) => {
    setGetBlogContent(blog);
  }
  return (
    <div>
      <div className="container">
        <Routes>
          <Route path="/" element={<HomePage data={getData}/>} />
          <Route path="/blog/:id" element={<BlogPost content={getBlogContent}/>} />
        </Routes>
      </div>
    </div>
  );
}
export default App;

Here, we set up the route to our HomePage and BlogPost. The home page has an empty path for the route, while the blog route shows the title “blog”, along with the ID of the specific React blog we will be viewing. In this case, it will be the name of the content.

Creating the landing and blog pages

We will create two folders in the src directory of the project folder: pages and components. In the former, we will have the HomePage and BlogPost pages, while the latter will contain the components that make up our application's two main pages. In HomePage.js, we have the following:

import {React,  useState, useEffect } from 'react';
import EmptyList from '../components/EmptyList';
import BlogList from '../components/BlogList';
import Header from '../components/Header';
import SearchBar from '../components/SearchBar';
import { blogList } from '../config/Api';

const HomePage = ({data}) => {
  const [blogs, setBlogs] = useState([]);
  const [searchKey, setSearchKey] = useState('');
  // Search submit
  const handleSearchBar = (e) => {
    e.preventDefault();
    handleSearchResults();
  };
  // Search for blog by category
  const handleSearchResults = () => {
   //handle search inputs
  };
  // Clear search and show all blogs
  const handleClearSearch = () => {
    blogList().then((res) => {
      setBlogs(res);
    })
    setSearchKey("");
  };

  // function to get selected blog content
 const BlogContent = (id) => {
  data(id);
}
  return (
    <div>
      {/* Page Header */}
      <Header />
      {/* Search Bar */}
      <SearchBar
        value={searchKey}
        clearSearch={handleClearSearch}
        formSubmit={handleSearchBar}
        handleSearchKey={(e) => setSearchKey(e.target.value)}
      />
      {/* Blog List & Empty View */}
      {!blogs.length ? <EmptyList /> : <BlogList blogs={blogs} content = {BlogContent}/>}
    </div>
  );
};
export default HomePage;

This will be the page structure of our landing page. It will contain a Header component and a SearchBar component, and it will render the content of our React blog. 

In BlogPost.js we have the following:

import {React} from 'react';
import Chip from '../components/Chip';
import EmptyList from '../components/EmptyList';
import '../index.css';
import { Link } from 'react-router-dom';

const Blog = () => {
 
  return (
    <>
      <Link className='blog-goBack' to='/'>
        <span> &#8592;</span> <span>Go Back</span>
      </Link>
    </>
  );
};
export default Blog;

This page describes the structure of the React blog view page. When the user selects content from the landing page, they can view more information on that content. This will be made possible using the BlogPost page.

React CMS Banner CTA

Creating our components

Next, we will define all components we will be using in our application. In the components directory, create the following files: Chip.js, EmptyList.js, BlogList.js, BlogItem.js, Header.js, and SearchBar.js.

The first component, Chip.js, will be responsible for containing the tags of specific blog content. We will use these tags to classify content and return content whose tags match the search field input when we wish to search through our React blog. The listed components will contain the following blocks of code:

//Chip.js

import React from 'react';
import '../index.css';
const Chip = ({ label }) => <p className='chip'>{label}</p>;
export default Chip;

//EmptyList.js

import React from 'react';
import '../index.css';
const EmptyList = () => (
  <div className='emptyList-wrap'>
    <img src='/assets/13525-empty.gif' alt='empty' />
  </div>
);
export default EmptyList;

This component defines an image that will be displayed when there is no content to be displayed on the page, or when the search query does not match the tags of any article. 

//BlogList.js

import React from 'react';
import BlogItem from './BlogItem';
import '../index.css';
const BlogList = ({ blogs, content }) => {
  return (
    <div className='blogList-wrap'>
      {blogs.map((blog) => (
        <BlogItem blog={blog} content={content}/>
      ))}
    </div>
  );
};
export default BlogList;

And in BlogItem.js, we have:

import React from 'react';
import { Link } from 'react-router-dom';
import Chip from './Chip';
import '../index.css';
const BlogItem = ({blog, content}) => {
  return (
    <div className='blogItem-wrap'>
    </div>
  );
};
export default BlogItem;

BlogItem.js and BlogList.js will handle rendering the content received from our CMS in our React application. In our Header.js, we have the following:

import React from 'react';
import '../index.css';
const Header = () => (
  <header className='home-header'>
    <h2>My Personal Blog</h2>
    <h1>
      <span>"</span> Take a look at my Content <span>"</span>
    </h1>
    <p>
      Read, enjoy <br /> and contribute.
    </p>
  </header>
);
export default Header;

For the SearchBar.js component, we have:

import React from 'react';
import '../index.css';
const SearchBar = ({ formSubmit, value, handleSearchKey, clearSearch }) => (
  <div className='searchBar-wrap'>
    <form onSubmit={formSubmit}>
      <input
        type='text'
        placeholder='Search By Category'
        value={value}
        onChange={handleSearchKey}
      />
      {value && <span onClick={clearSearch}>X</span>}
      <button>Go</button>
    </form>
  </div>
);
export default SearchBar;

And finally we will create a file called Api.js in a new directory, config, within the src folder. This file will handle all API requests for our CRUD operations:

import React from 'react'
export const blogList = async ()=>{
}

Styling our blog application

To style our application, add the following styles in index.css:

@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
*,
*::after,
*::before {
  box-sizing: border-box;
  margin: 0;
}
body {
  font-family: 'Montserrat', sans-serif;
}
button,
input {
  font-family: 'Montserrat', sans-serif;
}
.container {
  max-width: 1140px;
  width: 95%;
  margin: 0 auto;
  padding: 1rem 0;
}
/* BlogPost */
.blog-wrap {
  max-width: 700px;
  margin: 0 auto;
}
.blog-goBack {
  text-decoration: none;
  font-size: 0.8rem;
  color: #a9a9a9;
  font-weight: 500;
  margin-bottom: 2rem;
  display: block;
}
.blog-wrap header {
  text-align: center;
}
.blog-date {
  font-size: 0.8rem;
  color: #a9a9a9;
  font-weight: 500;
}
.blog-wrap img {
  width: 100%;
}
.blog-subCategory {
  display: flex;
  justify-content: center;
}
.blog-subCategory > div {
  margin: 1rem;
}
.blog-content p{
  margin-top: 1.5rem;
}
.blog-content h1, .blog-content h2, .blog-content h3, .blog-content h4, .blog-content h5, .blog-content h6 {
  margin-top: 1.5rem;
}

.blog-content img {
  margin: 10px 0;
}
/* Search Bar */
.searchBar-wrap, .create-form input {
  background-color: #f0f0f0;
  width: fit-content;
  margin: 2.5rem auto 4rem auto;
  padding: 0.5rem;
  border-radius: 5px;
}
.searchBar-wrap form, .create-form input {
  display: flex;
  align-items: center;
}
.searchBar-wrap input {
  background-color: #f0f0f0;
  outline: none;
  border: none;
}
.searchBar-wrap span {
  padding-right: 0.5rem;
  cursor: pointer;
}
.searchBar-wrap button, .btn {
  outline: none;
  border: none;
  padding: 0.3rem 1rem;
  border-radius: 5px;
  background-color: #0f52ba;
  color: #fff;
}

.btn{
  z-index: 5;
}
/* Header */
.home-header {
  text-align: center;
}
.home-header h2 {
  color: #0080ff;
  font-size: 2rem;
}
.home-header h1 {
  font-size: 3rem;
  color: #0f52ba;
  margin-bottom: 1rem;
}
.home-header h1 span {
  color: #b0c4de;
}
.home-header p {
  color: #a9a9a9;
  font-weight: 500;
}
/* Blog List */
.blogList-wrap {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 3rem;
}
@media (max-width: 768px) {
  .blogList-wrap {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (max-width: 600px) {
  .blogList-wrap {
    grid-template-columns: repeat(1, 1fr);
  }
}
/* Blog Item */
.blogItem-wrap {
  display: flex;
  flex-direction: column;
}
.blogItem-cover {
  width: 100%;
  height: 250px;
  object-fit: cover;
  border-radius: 20px;
  margin-bottom: 0.5rem;
}
.blogItem-wrap h3 {
  margin: 0.5rem 0 1rem 0;
  flex: 1;
}
.blogItem-desc {
  position: relative;
  max-height: 50px;
  overflow: hidden;
  padding-right: 0.6rem;
  font-size: 0.8rem;
  color: #a9a9a9;
}
.blogItem-desc::before {
  position: absolute;
  content: '...';
  bottom: 0;
  right: 0;
}
.blogItem-wrap footer {
  display: flex;
  align-items: center;
  margin-top: 1rem;
  justify-content: space-between;
}
.blogItem-link {
  text-decoration: none;
  color: inherit;
}
.blogItem-author {
  display: flex;
  align-items: center;
}
.blogItem-author img {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
  margin-right: 0.5rem;
}
.blogItem-author p {
  font-size: 0.6rem;
  color: #a9a9a9;
  font-weight: 600;
}
/* Empty List */
.emptyList-wrap {
  display: flex;
  justify-content: center;
}
.emptyList-wrap img {
  max-width: 250px;
  width: 100%;
}
/* Chip */
.chip {
  font-size: 0.7rem;
  background: linear-gradient(to right, #6190e8, #a7bfe8);
  color: #fff;
  padding: 0.3rem 0.5rem;
  border-radius: 5px;
  width: fit-content;
  text-transform: capitalize;
}

/* Create content form */
.create-form{
  position: absolute;
  height: 90vh;
  top: 0;
  z-index: 3;
  backdrop-filter: blur(5px);
  width: 80%;
  display: flex;
  justify-content: center;
  align-items: center;
}
.formcont{
  background: #fff;
  width: 80%;
  display: flex;
  padding: 10px 10px;
  flex-direction: column;
  height: 80%;
  justify-content: center;
}
.formcont textarea{
  border: 1px solid #ccc;
  outline: none;
  resize: none;
  padding: 10px;
  font-size: 0.7rem;
  border-radius: 5px;
  margin-bottom: 1rem;
  width: 100%;
  height: 12em;
}
.formcont span{
  display: flex;
}
.file{
  margin: 7px!important;
  padding: 0 !important;
}

Now, if we run our application using the npm start command in the CLI, we get a result similar to the below image:

Personal blog result after running npm start in the CLI.

Setting up ButterCMS

To get started with ButterCMS, you will first need to create a user account and log in to your dashboard on the ButterCMS website. ButterCMS has a content structure set up designed for creating React blog posts. With this, we do not need to create our own setup. To make use of React blog posts, select Blog posts from the left sidebar:

Where the "Blog Post" link is located in ButterCMS dashboard.

On the new page that opens up, we have an example blog post already created for us. If you wish to create a new blog post, click on the New Post button at the top-right and fill out the form on the page that opens. The WYSIWYG editor provided by the Blog Post type makes it easy to create content for a React blog site, including inserting media and videos into it.

Creating a new post in ButterCMS interface (specifically the blog engine).

Connecting our application to ButterCMS

To connect our application to ButterCMS, we will need the read token to fetch content from the CMS. This can be found in the settings tab on the dashboard. Copy and store this key in a .env file in your project directory. In Api.js, we will set up our fetch operation:

import React from "react";
import axios from "axios";

const read_token = process.env.REACT_APP_READ_TOKEN;

export const blogList = async () => {
  const url = `
  https://api.buttercms.com/v2/posts?auth_token=${read_token}`;
  return axios.get(url).then((res) => {
    return res.data.data;
  });
};

In HomePage.js, we can  get the data from this API request and store it in our blog state:

// get content from buttercms
useEffect(() => {
    blogList().then((res) => {
        setBlogs(res);
    })
} , []);

Then, we will render the content in BlogItem.js:

const BlogItem = ({blog, content}) => {
  return (
   <div className='blogItem-wrap' key={blog.title}>
      <img className='blogItem-cover' src={blog.featured_image} alt='cover' />
      <Chip label={blog.tags[0].name} />
      <h3>{blog.title}</h3>
      <p className='blogItem-desc'>{blog.summary}</p>
      <footer>
        <div className='blogItem-author'>
          <img src={blog.author.profile_image} alt='avatar' />
          <div>
            <h6>{blog.author.first_name+" "+blog.author.last_name}</h6>
            <p>{blog.created_at}</p>
          </div>
        </div>
        <Link className='blogItem-link' to={`/blog/${blog.title}`} onClick={()=>{content(blog)}}>
          ➝
        </Link>
      </footer>
    </div>
  );
};

This will render the content stored on our CMS. Below is an image showing some sample content I created in my blogreact collection:

New blog post displaying on the deployed website.

To be able to view our content via the arrow at the bottom right of the BlogItem component, we just need to make the following changes to BlogPost.js:

const Blog = ({content}) => {
  return (
    <>
      <Link className='blog-goBack' to='/'>
        <span> &#8592;</span> <span>Go Back</span>
      </Link>
      {content ? (
        <div className='blog-wrap'>
          <header>
            <p className='blog-date'>Published {content.created}</p>
            <h1>{content.title}</h1>
            <div className='blog-subCategory'>
              
                <div>
                  <Chip label={content.tags[0].name} />
                </div>
              
            </div>
          </header>
          <img src={content.featured_image} alt='cover' />
          <div className='blog-content' dangerouslySetInnerHTML={{__html: content.body}}></div>
        </div>
      ) : (
        <EmptyList />
      )}
    </>
  );
};

Here, we have the structure for the React blog content that will be displayed in the BlogPost.js file. The WYSIWYG editor returns its content in the form of HTML. We can then render this to produce the required results.

With this, we get a preview of the selected React blog content:

Selected blog post preview.

With our search field in the HomePage component, we will be able to search through our content and display results corresponding to a search query. To do this, edit the handleSearchResults function in HomePage.js as shown below:

 // Search for blog by category
  const handleSearchResults = () => {
    const filtered = blogs.filter((blog) => {
      return blog.tags[0].name.toLowerCase().includes(searchKey.toLowerCase());
    });
    setBlogs(filtered);
  };

React CMS Banner CTA

Questions about building a blog with React

How do you use React components in a blog?

React components are independent segments of code created for reuse to prevent a repetition of code blocks. In a blog use case, portions of the code such as the card display of articles on the blog can be created as a component to avoid having to repeat the same code for different posts. This component can then be imported to handle the rendering of as many posts that are to be displayed on a page. Also, sections such as the header and footer sections, which are the same for each article page, can be created as components and rendered with changes to their content to reflect the current article’s name and details on the article page.

How do you test your React blog?

React applications usually have different levels of sophistication. The first step to application testing is to outline sections of the application to be tested. There is not really any general method of classifying aspects of applications to be tested. However, if you are to carry out a test on a React blog application, these are the following sections you should consider:

  • Core features of the blog: Fetching blog content from whatever data source it is linked to and rendering individual content in a manner that boosts user experience
  • User interactions: Include testing scope for user interactions, such as actions to be handled by onClick events on the cards and the resulting action of opening and displaying the article content. Testing the search events using the on-site search field.
  • Components and state changes: Test the most used components that make up the blog and consider state changes and updates caused by prop changes in the blog. An example of this is the prop passed from the “all contents” display component of a React blog to the page where an individual content has its entire data displayed due to the user selecting a particular article from the list of items.

What should I learn before starting with React.JS?

By now, you are fully aware that React is one of the most popular JavaScript frameworks, and its popularity increases on a day-by-day basis as more people dive into software development. 

Starting out as a “newbie” developer can be very overwhelming, as there is tons of information out there at your disposal. So what should you learn before React.js? Here’s a list of the top 5 things:

Why should I learn React?

React's component-based architecture helps you save time and money on development. An interface can be disassembled into reusable parts that let developers create dynamic user interfaces. React developers are also in high-demand in the job market compared to other frameworks or libraries. Most employers around the world are looking for React developers when hiring software developers. According to ZipRecruiter: 

“As of Aug 15, 2022, the average annual pay for a React JS Developer in the United States is $108,485 a year.”

The flexibility of React is also another good reason for a developer to learn React. With React, you can build both web apps and mobile applications.

Can a beginner learn React?

React is easier to learn and more beginner-friendly compared to Angular and Vue. React uses JSX which has a syntax similar to HTML and JavaScript, making it beginner-friendly if you already have basic knowledge of HTML, CSS, and JavaScript. One of the best ways to learn React is by building projects and practicing regularly.

Closing thoughts

In this article, we learned how to build a blog with React. We also learned how to set up a blog web application with React, and manage the React blog’s content using ButterCMS. We also fetched data from the CMS to be displayed in the application. An on-site search feature to easily sort content based on its category was also built into our React blog application. 

I will love to know what else you would want to Build with React and ButterCMS. You can access the code for this tutorial in this Github repo.

For more articles on using React, read:

Or check out our React starter projects, our react headless CMS and blog engine.

Make sure you receive the freshest tutorials and Butter product updates in your inbox.
    
Chisom Uma

Chisom is a software developer and technical writer passionate about writing, and building front-end projects.

ButterCMS is the #1 rated Headless CMS

G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award G2 crowd review award

Don’t miss a single post

Get our latest articles, stay updated!