Off With Its Head!

(Using WordPress as a headless CMS)

What I'm going to talk about

  • What does Headless Mean?
  • Why PDG decided to go Headless
  • Let me show you how we did it!
  • What we learned from losing our heads

What does headless Mean?

Typically a traditional database driven CMS with UI for adding content

Content is accessible through a web API

The front end of the site uses JS to fetch the data from the API, and renders it into a UI

Headless vs Monolith

Monolithic

Headless vs Monolith

Decoupled (Headless)

But why complicate things?

Future Proofing

Speed 

Cross Platform Publishing

Developer Flexibility

Easier CI/CD

Losing our heads

The Original Stack

LAMP Stack Server => Web Server & Database

WordPress => CMS, Admin Console

Timber => Twig Templating engine for WordPress

Vanilla ES6, LESS, Babel, Webpack, Gulp  => 

Frontend

So What's the Problem?

A lot of bloated AJAX calls on a single page

Fighting with / Working around the default templates

But in the end it all came together, and it works, but it's less than ideal

How do we fix it?

WP REST API

React + Next.js Frontend

Super sweet design

What We are Building

Some Code...

{
"id": 10,
"date": "2017-05-26T22:30:56",
"date_gmt": "2017-05-26T22:30:56",
"guid": {
"rendered": "http://pagedev.dev/headers-post/"
},
"modified": "2017-05-26T22:30:56",
"modified_gmt": "2017-05-26T22:30:56",
"slug": "headers-post",
"status": "publish",
"type": "post",
"link": "http://pagedev.dev/headers-post/",
"title": {
"rendered": "Headers Post"
},
"content": {
"rendered": "<h1>This Is An H1 Tag</h1>\n<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>\n<h2>This Is An H2 Tag</h2>\n<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>\n<h3>This Is An H3 Tag</h3>\n<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>\n<h4>This Is An H4 Tag</h4>\n<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>\n<h5>This Is An H5 Tag</h5>\n<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>\n",
"protected": false
},
"excerpt": {
"rendered": "<p>This Is An H1 Tag Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. This Is An H2 Tag Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed … </p>\n<p class=\"link-more\"><a href=\"http://pagedev.dev/headers-post/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> “Headers Post”</span></a></p>\n",
"protected": false
},
"author": 1,
"featured_media": 0,
"comment_status": "open",
"ping_status": "open",
"sticky": false,
"template": "",
"format": "standard",
"meta": [],
"categories": [
1
],
"tags": [],
"_links": {}
},

https://(domain)/wp-json/wp/v2/posts

WordCamp Plug

I'll see ya there!

Some More Code...

import React from 'react'
import Layout from '../components/Layout'
import Articles from '../components/Articles'
import fetch from 'isomorphic-unfetch'

const Index = (props) => {
        return (
            <Layout>
                <Articles posts={props.posts} />
            </Layout>
        )
    }


Index.getInitialProps = async function() {
    const res = await fetch('http://pagedev.dev/wp-json/wp/v2/posts')
    const data = await res.json()

    console.log(data)

    return {
        posts: data
    }
};

export default Index

Layout, Fetching JSON, and passing to Components

Even More Code...

import React from 'react'
import Article from './Article'

class Articles extends React.Component {
render() {
    return(
        <div>
            <ul id="ideas" className="blocks">
                {this.props.posts.map((post) => (
                    <Article key={post.id} id={post.id} 
                             linkMask={`/p/${post.id}`} 
                             link={`/post?title=${post.title.rendered}`}
                             title={post.title.rendered} 
                             excpt={post.excerpt.rendered}  
                    />
                ))}
            </ul>
            </div>

        )
    }
}

export default Articles

The `Articles` Compontent

Seen Enough Code...?

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

class Article extends React.Component {
    render(){
        return(
            <div>
                <li id={this.props.id} className="article">
                    <Link as={this.props.linkMask} href={this.props.link}>
                        <a className="block">
                            <span className="eyebrow"><b>{this.props.cat}</b> / Technology </span>
                            <h4>{this.props.title}</h4>
                            <p>{this.props.excpt}</p>
                        </a>
                    </Link>
                </li>
            </div>

        )
    }
}

export default Article

The `Article` Compontent

Ok, this is the last of it...

import Layout from '../components/Layout.js'
import Hero from '../components/Hero'
import fetch from 'isomorphic-unfetch'

const Post = (props) => (
    <Layout>
        <Hero title={props.post.title.rendered} />
        <div dangerouslySetInnerHTML={ {__html: props.post.content.rendered} } />
    </Layout>
)


Post.getInitialProps = async function(context) {
    const { id } = context.query
    const res = await fetch(`http://pagedev.dev/wp-json/wp/v2/posts/${id}`)
    const data = await res.json()

    console.log(data)

    return {
        post: data
    }
}

export default Post

The Post Component / page

Lessons Learned

There are countless frontend options

Customizing REST endpoints

Decoupled CMS is a major paradigm switch

In Closing

WordCamp Sacramento 2017 - Off With It’s Head: Using WordPress As A Decoupled CMS For A Single Page Application

By Treighton Mauldin

WordCamp Sacramento 2017 - Off With It’s Head: Using WordPress As A Decoupled CMS For A Single Page Application

This talk will go into detail about when, why and how to use WordPress as a decoupled CMS backend with a React / Node frontend. This talk is equal parts tutorial, existential dive into when it is appropriate to use modern web application architecture, and lessons learned from my journey building a React SPA with a WordPress backend.

  • 1,183