Creating a Hybrid Single Page App in WordPress with VueJS

Power your WordPress application with Vue.js – the progressive JavaScript framework that now shares the stage with the likes of React and Angular.

In this article, I’ll show you how to integrate a Single Page Search App within your existing WordPress application with Vue.js – an extremely popular JavaScript framework for developing rich user interfaces. I’ll walk you through the entire process of building a Vue app from the ground up, interacting with the WordPress REST API and creating a search app with Vue’s reusable components.

If you’ve never worked with Vue or other modern JavaScript frameworks/libraries, it’s a great time to jump in. While the sheer number of frameworks urging you to up your JavaScript game can be quite overwhelming, Vue makes the journey nothing less than enjoyable.

Note: This article is intended for intermediate-advanced WordPress developers and assumes that you have a working knowledge of PHP, JavaScript, Vue.js and the WordPress REST API. If you’d like a refresher, I recommend that you read through the following:

Familiarity with Vue.js (2.x) is also essential. The Single Page App (SPA) uses many of Vue’s features such as single-file components, custom events, computed properties, lifecycle hooks, as well as Axios to interact with the WordPress REST API. If you’re new to Vue, it’s really easy to pick up, and you can get started in just a couple of hours with these tutorials:

Although Vue apps can be written in pure ES5 syntax, I will be using some new features of JavaScript introduced in versions ES6 and ES7. If you need to get up to speed with ES6, take a look at the following:

Whether you need to use modern JavaScript or Vue.js will completely depend on your requirements and preferences. My goal is to help you explore the possibilities of integrating Single Page Applications in WordPress with a practical Search app. So let’s get started!

Overview of the Vue SPA in WordPress

For this article, I’ve built a Single Page Search app in WordPress with Vue.js using a child theme of the Twenty Seventeen WordPress theme. You can download the child theme from here to follow along with the article.

The Search app can be rendered on any WordPress page using Custom Page Templates. The Page Template basically provides the Vue instance with an existing DOM element to mount on.

Integrating a Vue app in WordPress
Integrating a Vue.js App in WordPress with Custom Page Templates

The app thus adopts a hybrid or a mixed approach, where a server-side application (WordPress) also serves the frontend, but portions of it are rendered with a client-side application like Vue. This is different from a pure SPA that may use a headless or a decoupled CMS, where the server-side engine is only responsible for providing the content (through an API) and does not generate the HTML for rendering it.

Enhancing Search Experience with Vue.js

In a traditional search scenario, each request to the server causes the page to reload. However, with Vue.js or an SPA approach, the page is dynamically updated as the user interacts with the search but without the constant reloads. This makes for a very pleasing user experience. In fact, you can try it out right here in this pen that I’ve created:

My Vue Search app builds on the example from the pen above. So, let’s get a sense of the inner workings of the Search app before diving into the code.

Anatomy of the Vue Single Page Search App

The following infographic explains how the Search App is composed using several reusable components that interact with each other.

vue search spa internal structure
Internals of the Vue Single Page Search App

At a high-level, here’s what the various components do:

  • AppNavigation provides the router links for Vue Router to render the AppQuickSearch and AppCustomSearch components.
  • AppQuickSearch and AppCustomSearch work as parent components to AppFilterSwitches and AppGetPosts. However, they result in two completely independent Search applications, each equipped with its own of data, methods and properties.
  • AppFilterSwitches renders input toggle switches that may either be checkboxes or radio buttons depending on the parameters passed to it.
  • AppGetPosts fetches data from the WordPress REST API and filters it using the search key and toggle inputs. It then calls the AppDisplayPost component for each post in the filtered result.
  • AppDisplayPost renders the markup for an individual post item and displays it in the search results.

All of this takes place inside a Custom Page Template assigned to a specific page(s) in WordPress.

Using Vue Single-File Components

You may have seen many examples of Vue components created with the Vue.component syntax that use in-DOM templates or JavaScript template strings. However, to build the Search app in WordPress, I’ve made use of Vue’s powerful single-file components instead.

Single-file components have many inherent advantages – the ability to provide pre-compiled JavaScript (render functions) to the browser, syntax highlighting, case-insensitive component selectors, and component-scoped CSS to name a few. They also allow me to write modular and manageable code by splitting the codebase into multiple (.Vue) files.

Now that you have a fair idea of how things work, let’s start building the app.

Using Vue-CLI to Set Up a Local Development Workflow

Splitting the code into multiple (.Vue) files with single-file components will require the use of development tools like Vue Loader, Webpack, Babel etc. However, don’t let this throw you off as I’m going to keep things very simple and not deal with configuration files.

With the Vue-CLI you can quickly scaffold a Vue app that’s pre-configured with the best build tools for a modern frontend workflow. So, let’s set up the CLI first.

Step 1: Install Node.js

To use the Vue-CLI, you’ll require Node.js ( 8.x preferred, npm version 3+) installed on your system. You can download the installer for your platform from the Node.js downloads page here. Once you’ve set that up, test that the following commands work:

  • node --version and npm --version

Step 2: Install Vue-CLI

Next, open your system’s terminal (PowerShell in my case), and install the Vue-CLI (2.x) globally by running the npm install -g vue-cli command. Once complete, test that it works by running the vue --version command.

setting up vue cli 2
Setting up Vue-CLI (2.x) for creating Vue apps

In the image above, you’ll notice that I’ve also installed ESLint globally with npm install -g eslint. This is because I use the Visual Studio Code editor and its plugins to format and lint my JavaScript. You can use any code editor of your choice but configuring one with a JavaScript linter is highly recommended.

With the basic setup done, let’s create an app in WordPress with the Vue-CLI.

Scaffolding a Vue app with Vue-CLI

To create the Vue app, I’ve used the official webpack-simple vuejs-template. However, you may want to use a more sophisticated template based on your requirements.

The template sets up a Vue project with Webpack and configures it with a development server and other modern build tools. This provides us with an elaborate development workflow. Among other things, it allows us to write code using NextGen (ES6/ES7) JavaScript during development, but ship the compiled JavaScript bundle in ES5 for better browser compatibility.

Step 1: Set up the Vue app inside WordPress

To set up the Vue project, navigate to your WordPress theme or child theme using your system’s terminal. Here, I am using PowerShell (in Windows 10) integrated with the Visual Studio Code editor.

navigate to wordpress theme
Navigate to the directory of your WordPress theme

Step 2: Create a Vue app with the Webpack-Simple template

Next, run the command vue init webpack-simple project-name, substituting project-name with the name of your project (spa in my example), and follow the on-screen instructions.

vue app with vue init and webpack template
Scaffolding a Vue app with vue init and a Webpack template

Note: Skip this step if you’re following along with my vuetwentyseventeen child theme. It already contains the app in the spa project folder.

This creates the Vue project inside the <project-name> directory with configurations for modern build tools.

Step 3: Install Development Dependencies

If you head over to your WordPress theme using your code editor, and look inside the newly created project folder, among the many new files, you’ll notice a file called package.json. It basically lists all the development tools that the app will require. These tools still need to be installed though, and to do so, run the following:

  • cd project-name(substitue project-name with your project-folder)
  • npm install

NPM will then download and install all the required dependencies in a folder called node_modules

installing dev dependencies
Installing development dependencies with NPM

Note that we won’t be deploying any of these downloaded files in WordPress. They are only required during the development phase.

Step 4: Run the Webpack Dev Server alongside WordPress

The final step involves running the Webpack development server that was installed in the previous step. It may seem strange at first, but you have to run the development server (installed in the previous step), and keep it running along with your local WordPress server (XAMP, WAMP, VVV etc.).

Even though the Vue SPA is a client-side application, it initially needs to be served by a server, and the Webpack server will do this for us. Only when development is complete, will we serve the final JavaScript bundle through WordPress.

To start the development server, run the command npm run dev from the Vue project folder. You will then see the starter Vue app automatically open at localhost:8080 in your browser.

running webpack development server
Running the Webpack development server

The Vue Bundle (build.js)

If you look at the page source of the starter app in your browser, you’ll notice that the page contains only a single script file – build.js. This is because when you ran the development server, Webpack automatically compiled the Vue app and bundled it with the Vue library, and any other dependencies into a single JavaScript file.

However, do note that the file does not physically exist on your system, and is dynamically generated by Node and Webpack at runtime.

in memory javascript bundle generated by webpack
The JavaScript bundle dynamically generated at runtime by Webpack

To generate a physical build file that you can ship with your app, you have to run npm run build, which we will see at a later stage.

At this stage, we have a fully functional Vue app served by a development server from inside the WordPress theme folder. However, it has nothing to do with WordPress yet. So, let’s look at how you can integrate the Vue app with your WordPress theme.

Integrating the Vue SPA with WordPress

Integrating the Vue app with WordPress essentially requires three things:

  • A DOM element in WordPress for the Vue app to mount on
  • Enqueueing the Vue bundle in WordPress
  • Informing Vue about the DOM mount point in WordPress

Providing the DOM Element in WordPress for Vue

You may want to hook the Vue app on a single WordPress page or multiple pages, or conditionally. All Vue needs is a DOM element that exists on your WordPress page of choice.

For this, you can make use of the WordPress Template Hierarchy to decide the necessary template file that needs to be edited. In my example, I want the Search app to appear on any WordPress page that uses a specific Custom Page Template. You may instead want to use regular Page Templates to target specific pages based on your requirements.

The Custom Page Template templates/vue-search-app-template.php of my example child theme provides the DOM element, #wp-vue-app for Vue.

Registering the Vue App in WordPress

To let WordPress know about the Vue app, you have to register/enqueue the Vue bundle in WordPress. However, it is not feasible to generate a build file after every modification during development. For this, we can take advantage of the dynamic build that you saw earlier.

For as long as the Webpack development server is running, we can use the dynamic build path http://localhost:8080/dist/build.js to register the Vue script in WordPress.

The Webpack server will also automatically re-compile the Vue bundle and update the page as the app is modified.

register dynamic build during development script
Registering the dynamic build path in WordPress during development

This is the reason you have to run both the local WordPress server and the Webpack server during your development. When development is complete, you will have to modify the path to reflect the physical file that’s generated by running npm run build.

register build after development script
Registering the physical build file in WordPress after development

Also note, except for the final Vue bundle, none of the files in the Vue project folder needs to be shipped with your WordPress theme. They are required only during development, and when you have to make modifications to regenerate the Vue bundle.

In my theme example, I’ve registered the Vue bundle in the includes/enqueue-scripts.php

Informing Vue About the Mount Point in WordPress

Finally, to load the Vue app in WordPress, all that is required is to tell Vue where to mount itself. This is done by specifying the WordPress DOM element with the el option in main.js of your Vue project. Alternatively, you can also use the $mount method instead.

In my example, I mount the app on the #wp-vue-app DIV container of my Custom Page Template.

specifying the mount point in vue
Specifying the mount point in Vue using the ‘el’ option

And just like that, the starter Vue app will be rendered in WordPress.

injecting vue in wordpress
Rendering the starter Vue app in WordPress

Great, with the Vue starter app successfully injected into WordPress, you can now build pretty much anything with Vue. So, let’s get into the details of my Vue Search app.

Building the Single Page Search App in WordPress

If you look at the flowchart in the beginning of the article, you’ll be able to relate to the final Search app shown below:

vue search app wordpress
Building a Vue Search App in WordPress

The Project Folder Structure

To build this, I simply used the Vue starter app as a base. I got rid of spa/index.html and src/assets from the Vue project, and arrived at the following folder structure:

vue folder structure in wordpress
Folder structure of the Vue Search app in WordPress

If you’re wondering about the includes folder in the child theme, it’s because I use functions.php only to specify the WordPress Hooks, and define the respective callbacks in individual PHP files under includes/. I prefer this approach to dumping everything in a single functions.php file.

Adding Additional Dependencies for ESLint and ECMAScriptNext Features (Optional)

If you intend to use ESLint (which I highly recommend) or ESNext features like Async/Await, you’ll have to add some additional development packages to your project.

Configuring ESLint for WordPress and Vue

To configure ESLint, I’ve installed the eslint-plugin-vue plugin and the eslint-config-wordpress configuration. To do this, stop the development server (Ctrl+C), and run the following from inside your Vue project folder:
npm install --save-dev eslint eslint-plugin-vue
npm install --save-dev eslint-config-wordpress

Next, add the following to your .eslintrc.json file:
"extends": ["eslint:recommended", "wordpress", "plugin:vue/recommended"]

This will setup the JavaScript Coding Standards for WordPress, and the vue/strongly-recommended pre-defined linting rules for Vue. With modern editors like VS Code, this immensely helps me to catch and fix errors on the fly:

configuring eslint for vue wordpress
Catching errors with ESLint in Visual Studio Code

Adding Babel Presets for Async/Await

Babel presets are out of the scope of this article, but the following will basically allow you to use ES7 Async/Await in your Vue app. For this, you have to add babel-preset-vue-app by running:
npm install --save-dev babel-preset-vue-app

Then, add the preset vue-app to the .babelrc file in your Vue project folder:
"presets": [["env", { "modules": false }], "stage-3", "vue-app"]

When you’re done, don’t forget to start the development server with npm run dev. My example Vue child theme has all of this already configured for you, so you only need to install the packages by running npm install from within the spa directory.

Making Local WordPress Data Available to Vue

Vue is ultimately JavaScript that runs in your browser, and so, it won’t have access to any data in WordPress. To make local WordPress data available to the Vue app, you’ll have to use the good old wp_localize_script WordPress function.

I’ve done this in the includes/enqueue-scripts.php of my vuetwentyseventeen child theme.

The gist above should be pretty self-explanatory with all my comments, so I’ll instead focus on the data that I’ve made available to my Vue app:

  • wpData.template_directory_uri – to build the file path for static assets (like images) in the theme folder
  • wpData.rest_url – URL to retrieve posts from the WP REST API
  • wpData.app_path – the SPA WordPress page to build relative links
  • wpData.post_categories – to render checkboxes for filtering posts

With this out of the way, let’s finally explore the single-file components of the Search app.

Building the Search App with Vue Single-File Components

With the Search app structure chalked out, the first component that I actually built was the AppDisplayComponent. I started off with a very basic component to display only post titles using the JavaScript Fetch APIand the WordPress Posts resource/wp/v2/posts.

A Basic Version of the AppDisplayPost Component

a basic component to get started
A very basic version of the AppDisplayPost component to get started

And to render it on the WordPress page, I deleted all of the starter content in App.vue, and invoked the AppDisplayPost component as shown below:

initial version of app.vue component
A very basic App.vue to render the AppDisplayPost Component

However, not everything worked on the first try (or even a few after that), which is when the Chrome extension of the Vue DevTools came to my rescue. I’d suggest you have it installed as well, as it will allow you to debug and inspect your Vue application with a more user-friendly interface rather than just logging everything to the console.

debugging with vue devtools
Debugging Vue applications with Vue Devtools

I’d also recommend that you use a tool like Postman to interact with the WP REST API. It’ll save you a lot of time understanding the API response, and provides the data in a format that’s much easier to view. This is what I mean:

rest client postman interacting with wordpress rest api
Using a REST client like Postman to interact with the WordPress REST API

Rendering posts from WordPress in the Vue app did take me a while to set up initially, but after a few rounds between the Vue DevTools and Postman, I was good to go. At this point, I also decided to extend the API response to add custom content.

Extending the WordPress REST API for Custom Content

The default response from the REST API is pretty comprehensive; however, it’s likely that it may not satisfy all your requirements.

For example, you may want to display information such as the author name, comments and the featured image of a post. If you make a GET request to the posts route in Postman (or your preferred REST client), you’ll notice that these are not directly available in the default response. While extending the API is an option here, you can also retrieve these by simply adding the _embed=true parameter to the resource – wp/v2/posts?_embed=true. With _embed, the API will collate all metadata marked with embeddable: true in the post response.

For my Vue Search app, I decided to extend the API instead, and added the following custom content:

extending the wordpress rest api
Extending the default response of the WordPress REST API

If you look at the final version of the AppDisplayPost component in my child theme, you’ll notice that I’ve used a field vue_meta which is not part of the default response. It was added with the register_rest_field function in includes/extend-api.php of the child theme. The code in there is fairly basic, and to know more about extending the API, take a look at the Modifying Reponses section of the REST API Handbook.

With this, I then moved the logic for retrieving posts to the AppGetPosts component, and only used AppDisplayPost for rendering the required content of individual post items.

The AppGetPosts Component to Consume Data from the REST API

Separating the logic for data retrieval also meant passing the Posts array to AppDisplayPosts via props.

Then, in AppGetPosts I invoked AppDisplayPost for each post in the posts array.

I also decided to use Axios instead of the native Fetch API to retrieve posts from the WordPress REST API. I’m more comfortable using Axios to consume data from APIs but you can also choose to use jQuery (which is already included in WordPress) to make AJAX calls.

Note: To use Axios, you’ll have to install it as a production dependency by running npm install axios in your Vue project folder.

Retrieving only Specific Fields from the WordPress REST API

I recently discovered that you can use the _fields parameter to retrieve only the required fields from the API response. This significantly reduces the payload size, especially when you don’t want the post content in the JSON response. To do this, simply add the _fields parameter with a comma-separated list of field-names as values – wp/v2/posts?_fields=id,title,excerpt

retrieve specific fields rest api
Using the _fields parameter to selectively include fields in response JSON

The _fields parameter still needs to find its way into the REST API Handbook, so you might want to keep an eye on it.

Retrieving All Posts from the WordPress REST API

Currently, there is no way to retrieve all posts from the WordPress REST API. To do this, you’ll have to run a loop and make multiple requests to the API until the required data has been fetched.

To calculate the number of API requests, I made use of the Pagination Parameter per_page=100 and the X-WP-Total header field, which provides the total number of records in the collection. The per_page parameter is currently capped at at 100 records, which is why we need to make multiple requests to the API when there are more than 100 posts. You’ll see this in action in the get_posts method of the AppGetPosts component in the gist below:

In the gist above, get_posts is automatically invoked when the component is mounted. I’ve made use of the ES7 Async/Await feature to mark the method as an aynchronous function that contains await expressions.

You’ll notice that the very first Axios request is marked with await
const response = await axios( ... ) . This prevents the subsequent lines of code from executing until the request is resolved. I did this to retrieve the x-wp-total header to calculate the required number of API requests.

The second usage of await is at the end, where it waits for all Promises to resolve with Promise.all before rendering the data on the page. However, you can also render data as soon as it’s available like this:

With the required data available, I then added the search input box and filter logic in a computed property. In the gist below, notice how FilteredResults replaced wpPosts to invoke the AppDisplayPost component.

The AppQuickSearch and AppFilterSwitches Components

With AppGetPosts nicely taking care of the data retrieval and filtering, I then moved the user input to the parent component AppQuickSearch and used props to pass the data down the chain.

I created a new component AppFilterSwitches to render category filters using the localized WordPress wpData object. The component emits a custom event onFilterToggle that the parent component AppQuickSearch must listen to.

Finally, all the components were amalgamated in AppQuickSearch

In the end, I simply enqueued the final build generated by running npm run build.

generating final app build
Generating the final build with npm run build

If you’ve come this far, you should feel comfortable exploring the rest of the app on your own. The final versions of the components do have a lot more but they are built on all that you just saw.

Exploring Routing and Keep-Alive Components

While I could have ended the app with just the quick search, I’ve added another component AppCustomSearch for you to explore Vue routing, and how the various components can be reused easily with the help of props.

The Vue Router is beyond the scope of this article, but you can find the functionality for routing in spa/src/app-routes.js. It provides the mapping between the AppQuickSearch, AppCustomSearch and the navigation links. If you do end up using the Vue router on a WordPress page, just remember that Vue will use the URL '#' to simulate a page, so that it doesn’t reload when you switch between the router links. If you try to exclude the hash (see my comments in app-routes.js), the WordPress Rewrite API will take over, and you’ll end up with a 404 not found.

The App.vue component hosts the AppNavigation and router-view components. You’ll also notice that the router-view is wrapped with keep-alive to preserve the component states and avoids re-rendering of the AppQuickSearch and AppCustomSearch when you switch between them.

That’s it!

Summing Up

I hope you’ve found this article useful. Feel free to play around with my Vue Search app which you can download as part of the twenty-seventeen child theme here. You can use it as a playground to practice and grow your skills in WordPress and modern JavaScript.

Karan Gupta
Have you integrated Vue.js in WordPress? Let us know in the comments below.