Creating a WordPress Theme Using REST API and Vue.js

Creating a WordPress Theme Using REST API and Vue.js

John Hewick

I bet you didn’t think it wasn’t entirely feasible to build a WordPress theme using both the REST API and Vue.js. While there are more complex ways of creating themes, this method is surprisingly effective, and easy!

To overcome any potential issue with SEO, code partitioning and performance, this guide covers the setup of a very simple theme. It’s no thrills but provides a platform to showcase the content before Vue takes over to power the page. This approach allows Google to crawl the page but users will see the Vue application. Vue-resource will be used to fetch content from WordPress REST API while vue-router takes control of all navigation features.

Code for WordPress

Code for the WordPress part of this theme will include the following files; style.cs, functions.php and index.php.

Index.php is concerned with the basic HTML content of the page which will be viewed by the search engine crawlers. Add the following code:

<!DOCTYPE html>

<html <?php language_attributes(); ?>>


<meta charset=”<?php bloginfo( ‘charset’ ); ?>”>

<meta name=”viewport” content=”width=device-width, initial-scale=1″>

<link rel=”profile” href=”″>

<link rel=”pingback” href=”<?php bloginfo( ‘pingback_url’ ); ?>”>

<?php wp_head(); ?>



<div id=”content”>


if ( have_posts() ) :

if ( is_home() && ! is_front_page() ) {

echo ‘<h1>’ . single_post_title( ”, false ) . ‘</h1>’;


while ( have_posts() ) : the_post();

if ( is_singular() ) {

the_title( ‘<h1>’, ‘</h1>’ );

} else {

the_title( ‘<h2><a href=”‘ . esc_url( get_permalink() ) . ‘”>’, ‘</a></h2>’ );







<div id=”app”></div>

<?php wp_footer(); ?>



Any server rendered content is placed in #content div – this is hidden but used by the search engines for ranking.

Code for Vue

As you can see, the WordPress theme is straightforward, now it’s time to bootstrap the Vue application into the #app div. You’ll find the Vue part of the theme in the rest-theme folder. Initially, you’ll have to create the src/main.js file as a main app component with a template. Use the following code:

template: ‘<theme-header></theme-header>’ +

‘<div class=”container”><router-view></router-view></div>’ +


While you might use Vue’s Async Components to load routes as they are needed in larger sites, it’s recommended to output routes for smaller sites using wp_localize_script and functions.php:

function rest_theme_scripts() {


wp_localize_script( ‘rest-theme-vue’, ‘wp’, array(

‘root’      => esc_url_raw( rest_url() ),

‘nonce’     => wp_create_nonce( ‘wp_rest’ ),

‘site_name’ => get_bloginfo( ‘name’ ),

‘routes’    => rest_theme_routes(),

) );


add_action( ‘wp_enqueue_scripts’, ‘rest_theme_scripts’ );

function rest_theme_routes() {

$routes = array();

$query = new WP_Query( array(

‘post_type’      => ‘any’,

‘post_status’    => ‘publish’,

‘posts_per_page’ => -1,

) );

if ( $query->have_posts() ) {

while ( $query->have_posts() ) {


$routes[] = array(

‘id’   => get_the_ID(),

‘type’ => get_post_type(),

‘slug’ => basename( get_permalink() ),





return $routes;


From here you can use Vue router to bootstrap the routes after the initial page load:

for (var key in wp.routes) {

var route = wp.routes[key];

router.on(route.slug, {

component: Vue.component(capitalize(route.type)),




Next you’ll have to create elements that communicate with the route.type – they may include posts or product pages. This will grab the content from the REST API before rendering the output:


<div class=”post”>

<h1 class=”entry-title” v-if=”isSingle”>{{ post.title.rendered }}</h1>

<h2 class=”entry-title” v-else><a v-link=”{ path: ‘/’ + post.slug }”>{{ post.title.rendered }}</a></h2>


<div class=”entry-content”>

{{{ post.content.rendered }}}






export default {

props: {

post: {

type: Object,

default() {

return {

id: 0,

slug: ”,

title: { rendered: ” },

content: { rendered: ” }






ready() {

// If post hasn’t been passed by prop

if (! {


this.isSingle = true;




data() {

isSingle: false



methods: {

getPost() {

this.$http.get(wp.root + ‘wp/v2/posts/’ + this.$route.postId).then(function(response) { =;


}, function(response) {

// Error







Once you’ve implemented this code, it’s still a no-frills theme and there’s plenty of elements to add. From including WordPress menus to adding load indicators and transitions, these more advanced features should be implemented along the way.

While it’s not the conventional approach, this method does highlight the ability to create your own themes using the REST API. Give it a go for yourself and you may be surprised at the outcome.


John Hewick

Author John Hewick

More posts by John Hewick