GSD

How to Build a Serverless, SEO-Friendly React Blog With Netlify

Posted by Jake Lumetta on October 10, 2023

Serverless application architectures are gaining in popularity and it's no mystery why. Developers are able to build and iterate on products faster when they have less infrastructure to maintain and don't need to worry about server maintenance, outages, and scaling bottlenecks.

In this tutorial, you’ll learn how to use React, ButterCMS, and Netlify to create a serverless, SEO-friendly, CMS-powered blog. You'll also learn about Netlify, serverless architecture, and why it's an excellent choice for SEO-friendly apps.

What is Netlify?

Netlify is a platform that allows developers to securely and easily build, test, deploy, host, and manage web applications. Netlify connects to the repository containing the source code provided by developers, then begins the build process and delivers the build to content delivery networks (CDNs).

Netlify is an excellent option for quickly deploying serverless applications because it includes several features, such as:

  • Serverless platform: Netlify provides a serverless platform that manages the application’s infrastructure, allowing developers to focus on building serverless applications.

  • Content delivery network (CDN): Netlify provides a content delivery network that enables the rapid delivery of serverless functions.

  • Monitoring: Developers can use the Netlify monitoring tool to keep track of their application performance in real time and identify any issues that may impact the user experience.

  • Scalability: Netlify automates the scaling of your serverless functions for your applications so developers don't have to worry about application performance as the number of users increases.

In addition, Netlify offers several pricing plans that you can choose from depending on your project’s requirements. They include:

  • Starter plan - Free

  • Pro plan - $19 per member/month

  • Business plan - $99 per member/month

  • Enterprise plan - Custom pricing

Netlify pricing chart

To get started with Netlify, first create an account and log in. You will be directed to a page where you can connect your source code repository:

Netlify sign up

What is serverless architecture?

A lot of work goes into managing servers such as making software updates, ensuring data is backed up, and maintaining security—all of which could be a hassle for developers. With a serverless architecture, developers can outsource this huge load of work to a service provider like Netlify and focus on writing the code for their applications instead.

Serverless architecture is a technique that saves developers the stress of managing their application’s underlying infrastructure and lets them focus on building their applications. 

See how Butter's simple content API works with your React app

What makes it a good choice for SEO-friendly apps?

Serverless architecture offers a number of benefits that make it a good choice for building SEO-friendly apps. Such benefits include:

  • Reduction in development time: Using a traditional server-based architecture to handle SEO-friendly apps results in developers spending a significant amount of time manually configuring the entire application's server, which reduces the focus and attention spent on ensuring the application is SEO-friendly. Serverless architecture is a better option because it simplifies the structure of the application's code by breaking it down into smaller functions that can be easily managed, scaled, maintained, and optimized for SEO. It also simplifies the testing and integration of SEO strategies for developers.

  • Cost-effective: When developing SEO-friendly apps, there are several costs to consider, such as tools that track or monitor keyword rankings, content creation, storage, infrastructure, link building, and so on. When you use a serverless architecture for your SEO-friendly apps, you have the advantage of only paying for the resources that you use at any given time, saving costs that can be invested in ensuring good SEO for your application.

  • Auto-scaling: Manually handling sudden spikes in traffic as your application scales can be daunting and have an impact on the application's performance by causing slow loading times. This could have an impact on the application's user experience as well as its SEO ranking. Luckily, serverless architectures are a great solution to this problem because they allow for automatic scaling in response to changes in the workloads or traffic of the application without affecting its performance.

  • Third-party integrations: With serverless architectures, you can easily integrate your application with third-party services that provide several SEO best practices such as schema markup, content, mobile or website speed optimization, and so on.

Building an SEO-friendly React blog with Netlify and ButterCMS

Here, we’ll build a serverless and SEO-friendly blog application using React, ButterCMS, and Netlify. The tutorial's finished code is available on GitHub.

Prerequisites

To follow along with this tutorial, ensure you have the following:

Getting started

We'll be using Vite to set up our React project.

Create the new React project by running this command:

npm create vite@latest react-serverless-blog -- --template react

Then, run the following commands:

cd react-serverless-blog
npm install
npm run dev

Creating routes

Our blog needs two screens: one for listing all posts and another for displaying individual posts. Create BlogHome.jsx and BlogPost.jsx components in the src directory:

Add this code to the BlogHome.jsx file:

import React from 'react';

function BlogHome () {
  return (
    <div> Home </div>
  )
}
export default BlogHome;

Add this code to the BlogPost.jsx file:

import React from 'react';

function BlogPost () {
  return (
    <div> Post </div>
  )
}
export default BlogPost;

Creating a React app doesn't offer routing out-of-the-box, so we'll add react-router-dom:

npm install react-router-dom

In the source folder, create a new file called routes.jsx. We'll create routes for the blog home page with and without page parameters, as well as the individual post page:

import React from "react";
import { Route, createRoutesFromElements } from "react-router-dom";

import App from "./App";

const Routes = () =>
  createRoutesFromElements(
    <Route path="*" element={<App />}>
      {/* <Route path="/" element={<BlogHome />} />
      <Route path="/p/:page" element={<BlogHome />} />
      <Route path="/post/:slug" element={<BlogPost />} /> */}
    </Route>
  );

export default Routes;

Next, we'll update main.jsx so it uses our routes when initializing the application:

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import router from "./routes";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <RouterProvider router={createBrowserRouter(router())} />
  </React.StrictMode>
);

And finally, we'll update App.jsx so it nests child components specified in our routes:

import { Routes, Route } from "react-router-dom";
import BlogHome from "./BlogHome";
import BlogPost from "./BlogPost";

function App() {
  return (
    <div className="App">
      <div className="App-header">
        <h2>My blog</h2>
      </div>
      <div>
        <Routes>
          <Route path="/" element={<BlogHome />} />
          <Route path="/p/:page" element={<BlogHome />} />
          <Route path="/post/:slug" element={<BlogPost />} />
        </Routes>
      </div>
    </div>
  );
}

export default App;

Building the blog

Next, we’ll slide in ButterCMS to bring our blog to life. ButterCMS provides an API that lets you easily fetch blog posts, categories, tags, and authors.

First, we'll install the ButterCMS JS client:

npm install buttercms --save

Next, we’ll fetch posts from ButterCMS and render them. You'll need to obtain your API token from your ButterCMS dashboard. Log in and select React from the list of stacks offered by ButterCMS:

ButterCMS welcome page

Then, copy your API token:

ButterCMS API token location

Create a butter.js file to store your API token as well as the methods we'll use to fetch posts by page and posts by slug. Copy and paste the following code into the file, replacing <API token> with the API token you copied:

import Butter from "buttercms";
const butter = Butter("b41053deb541f5725fa2e56763a02e2ce4f1cc36");

export function fetchPostsByPage(page, setState) {
  butter.post.list({ page: page, page_size: 10 }).then((resp) => {
    setState({
      loaded: true,
      resp: resp.data,
    });
  });
}

export function retrievePostsBySlug(slug, setState) {
  butter.post.retrieve(slug).then((resp) => {
    setState({
      loaded: true,
      post: resp.data.data,
    });
  });
}

Next, copy and paste the following code into your BlogHome.jsx file:

import React, { useEffect, useState } from "react";
import { fetchPostsByPage } from "./butter";
import { useParams, Link } from "react-router-dom";

function BlogHome() {
  const params = useParams();
  const [state, setState] = useState({
    loading: false,
    resp: null,
  });

  useEffect(() => {
    let page = params.page || 1;
    fetchPostsByPage(page, setState);
  }, [params.page]);

  return state.loaded ? (
    <>
      <div>
        <ul style={{ listStyleType: "decimal-leading-zero" }}>
          {state.resp?.data.map((post) => {
            return (
              <li>
                <div key={post.slug}>
                  <Link className="blog-links" to={`/post/${post.slug}`}>{post.title}</Link>
                </div>
              </li>
            );
          })}
        </ul>
        <br />
        <div>
          {state.resp.meta.previous_page && (
            <Link to={`/p/${state.resp.meta.previous_page}`}>Prev</Link>
          )}
          {state.resp.meta.next_page && (
            <Link to={`/p/${state.resp.meta.next_page}`}>Next</Link>
          )}
        </div>
      </div>
    </>
  ) : (
    <div>Loading...</div>
  );
}
export default BlogHome;

Next, we'll update BlogPost.jsx to fetch and display posts based on the route:

import React, { useEffect, useState } from "react";
import { retrievePostsBySlug } from "./butter";
import { Helmet } from "react-helmet";
import { useParams, Link } from "react-router-dom";

function BlogPost() {
  const params = useParams();
  const [state, setState] = useState({
    loaded: false,
    post: null,
  });

  useEffect(() => {
    retrievePostsBySlug(params.slug, setState);
  }, []);

  if (state.loaded) {
    const post = state.post;

    return (
      <div>
        <Helmet>
          <title>{post.seo_title}</title>
          <meta name="description" content={post.meta_description} />
          <meta name="og:image" content={post.featured_image} />
        </Helmet>
        <h1>{post.title}</h1>
        <div dangerouslySetInnerHTML={{ __html: post.body }} />
        <Link className='goback' to="/"> Back </Link>
      </div>
    );
  } else {
    return <div>Loading...</div>
  }
}

export default BlogPost;

Head elements

Our blog is working but our post pages don't have properly set HTML titles or meta tags. To fix this, we'll use React Helmet.

First, install the package:

npm install --save react-helmet

Import the Helmet module in our BlogPost.jsx file and use it in our render() method to set HTML tags that should be in the <head>:

  <Helmet>
    <title>{post.seo_title}</title>
    <meta name="description" content={post.meta_description} />
    <meta name="og:image" content={post.featured_image} />
  </Helmet>

Here's what the complete code for the BlogPost.js component looks like. Inspect the DOM to verify that tags are getting set correctly.

import React, { useEffect, useState } from "react";
import { retrievePosts } from "./butter";
import { Helmet } from "react-helmet";

function BlogPost({ params }) {
  const [state, setState] = useState({
    loaded: false,
    post: null
  });

  useEffect(() => {
    retrievePosts(params.slug, setState)
  }, [])

  if (state.loaded) {
    const post = state.post;

    return (
      
      <div>
        <Helmet>
          <title>{post.seo_title}</title>
          <meta name="description" content={post.meta_description} />
          <meta name="og:image" content={post.featured_image} />
        </Helmet>
        <h1>{post.title}</h1>
        <div dangerouslySetInnerHTML={{__html: post.body}} />
      </div>
    );
  } else {
    return (
      <div>
        Loading...
      </div>
    );
  }
}

export default BlogPost;

Adding blog posts to ButterCMS

Let’s proceed to ButterCMS to add three blog posts.

Creating the first post

Go to your ButterCMS dashboard and select Blog Posts from the sidebar menu. Click on New Post

Select New Post button

Then add blog post details including a title and content. The title of this tutorial will be "The Benefits of Meditation: A Guide to Finding Inner Peace." The content of the post can be found in the white box (you can use one of your choice).

Input blog content into wysiwyg and associated fields

ButterCMS includes a Metadata section that includes information about the Author, Publish Date, Categories, Tags, Summary, Featured Image, and Featured Image Alt Text. Let’s add the following:

  • “Mental Health” as the category

  • A meditation tag

  • A brief summary of the post (Use any of your preferences)

  • An uploaded image (You can either upload your own image or use the one provided by ButterCMS)

  • Image alt text of “woman meditating”.

Fill in Metadata fields

ButterCMS also has an SEO section that includes the fields URL Slug, SEO Title, and Meta Description. After adding and saving your metadata, ButterCMS will generate SEO for you. For this tutorial, we'll be using:

  • URL Slug - the-benefits-of-meditation-a-guide-to-finding-inner-peace 

  • SEO Title - The Benefits of Meditation

  • Meta Description - Add a description 

Once you are done, click on the Publish button:

Publish the post

Creating the second post

To create another post, navigate to Blog Posts in the side menu and click on New Post:

Select new post button again

Add your title and content:

Input content

Add the details for your metadata:

Insert metadata

Add details for your SEO, then click on Publish.

Insert SEO field content

Creating the third post

Go back to Blog Posts and click on New Post:

Select New Post again

Add your title and content:

Add content

Then, add your metadata and click Save Draft:

Insert metadata field content

Afterwards, ButterCMS will automatically generate your SEO:

Insert SEO metadata fields

Running the application

Start the application by running this command in the root directory of the project:

npm run dev

See how Butter's simple content API works with your React app

Go to the URL your application is running on localhost:

Local blog application running

Finally, the blogs have been fetched from ButterCMS and rendered successfully. Click on the first link that redirects to the first blog post we added to ButterCMS:

Localhost rendered individual blog post page

Prerendering

Our blog is set up, but crawlers from search engines and social networks don't always execute JavaScript, so our blog has terrible SEO.

There are a number of ways to address this, including setting up server-side rendering and pre-rendering services like Prerender.io. But these options all require setting up and maintaining a Node.js server. Luckily, Netlify offers built-in prerendering which allows us to keep our code "serverless" while still making sure our blog is optimized for crawlers.

Deploying to Netlify

Before deploying to Netlify, we'll create a production build of our app:

npm install
npm run build

When you run those commands, a production build directory called dist will be generated:

Dist directory

Push your code to GitHub, then go to Netlify, sign in, and click on Add new site and select Import an existing project from the list of options:

Select import existing file

Next, link your GitHub to Netlify by clicking the GitHub button:

Select GitHub

Search the repository that contains the source code for the project by the name “react-serverless-blog” and select it:

Name project

Next, configure the site settings. Select the main branch, enter the “yarn build” command,  enter the dist directory, and click on Deploy site:

Select Deploy site

Once you do that, Netlify starts a build process and your application will be deployed, live, and available to use. The link to the app is written in green. 

Link to app

To enable prerendering, go to your app settings, select Build & deploy, and click Edit settings under Prerendering:

Navigating to Prerendering

Tick the checkbox saying Enable prerendering with Netlify and click on Save:

Select Enable prerendering with Netlify

The link to the application is displayed at the top-left corner of the page:

Link to application

Visit the application URL at https://react-serverless-application.netlify.app/:

Live blog application homepage

Your blog will work when it is loaded from the homepage, but if you try to load a post directly, you'll get a 404 page:

page not found

Remember that our app is serverless? To fix the 404 error we need to create a URL rewrite rule that servers up index.html no matter what URL the browser requests.

To do this, we'll create a _redirects file in our build directory with the following line:

/*    /index.html   200

Re-upload the contents of your build directory, and all your pages should load. Check out our live example:

Live individual blog post page

To make sure this file gets redirected each time we build our app, we can update our build script in package.json:

"build": "react-scripts build && echo '/*    /index.html   200' > build/_redirects"

Wrapping up

ButterCMS and Netlify provide an excellent solution for developers who want to streamline the process of building and optimizing SEO for their blog sites. By integrating Netlify's serverless platform with ButterCMS, developers can simplify app development and deployment, reduce costs, and focus on content creation while Netlify manages the infrastructure.

For further guidance on using ButterCMS, check out the official blog website, which offers a range of tutorials and resources.

Make sure you receive the freshest tutorials and Butter product updates.
Jake Lumetta

Jake is the CEO of ButterCMS. He loves whipping up Butter puns and building tools that make developers' lives better.

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!