Build a React Universal Blog with Next.js and ButterCMS

You know the story. You've sold your client on the major benefits of React, such as quick-loading screens and maintainable JavaScript code. And now that you've built a great React app for your client, they want to integrate a blog directly into the app; one that lives in a subdirectory (not a subdomain) for its SEO benefits. (Having your blog live in a subdirectory (e.g., /blog) versus a subdomain (e.g., blog.domain.com) has significant SEO benefits because if your blog is on a separate domain, then your actual website and your blog don't share link value and domain reputation.) The blog also needs to be able to serve crawlable pages so Google and social media websites can parse its title and meta tags. The typical solutions for doing this are by using a pre-rendering service like Prerender.io or implementing custom server-side rendering with React.

Another alternative is Next.js, a relatively new framework for building universal React Web apps. Next.js provides out-of-the-box tools for server-side rendering, including setting HTML tags for SEO and fetching data before rendering components. Read more about the philosophy behind Next.js here.

In this article you'll learn how to build a CMS-powered blog using React, Next.js, and ButterCMS. The finished code for this tutorial is available on GitHub.

ButterCMS is a headless CMS and blogging platform that lets you build CMS-powered apps using any programming language, including Ruby, Rails, Node.js, .NET, Python, Phoenix, Django, React, Angular, Go, PHP, Laravel, and Meteor. You can think of ButterCMS as similar to WordPress except that you build your website in the language of your choice and then plug in the dynamic content using an API.

To get started, you'll need access to the command line interface (CLI) of a Linux-based server (your development server). Once there, create a new directory for your app (it doesn't matter where, you just need a folder for all your code), set it as your current working directory, and add a package.json file containing the following code:

{
  "name": "react-blog"
}

Then, install Next.js and React. As of the time of this writing, you'll want to install Next.js so you can set up dynamic routes later:

npm install next@beta react react-dom --save

The command installs the three packages (next, react, and react-dom). The --save option is standard and just updates your package.json file for you so that another developer inheriting the code can run `npm install` to get all the dependencies. Add this script to your package.json file:

{
  "scripts": {
    "start": "next"
  }
}

Next.js treats every .js file in the ./pages directory as a page. Let's set up a basic home page by creating an index.js file in the ./pages/ directory (in other words, the pages directory is a subdirectory to the working directory you started with) and loading it with this code:

export default () => (
  <div>My blog homepage</div>
)}

Then just run it:

npm run start

Open up a Web browser and visit http://localhost:3000 to view your running app.

Finally, let's create a static post component to test out another route. Create a post.js file with the following code in the ./pages directory and make sure your browser can successfully load it at http://localhost:3000/post:

export default () => (
  <div>A blog post</div>
)

Fetching Blog Posts

Next you'll integrate ButterCMS so you can fetch and render blog posts dynamically.
First install the ButterCMS Node.js API client and restart your server with the following commands at the development server's CLI:

npm install buttercms --save
npm restart

Next, use the following code to update the index.js to be a React component that fetches and displays posts using the ButterCMS SDK:

import React from 'react'
import Link from 'next/link'
import Butter from 'buttercms'


const butter = Butter('de55d3f93789d4c5c26fb07445b680e8bca843bd')


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>
          )
        })}


        <br />


        <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 different routes using the built-in <Link> component. GetInitialProps also receives a context object with various properties — you access the query property for handling pagination.

Roger Jin Software Architect at ButterCMS.com and co-author of Microservices for Startups (microservicesforstartups.com)
 

Comments