Handling Form Submissions in WordPress with Admin-Post and Admin-Ajax

WordPress provides incredible support for you to work with form submissions in your application. Whether you add a form in the admin or public facing areas, the built-in mechanism with the admin-post and admin-ajax scripts will allow you to handle your form requests efficiently.

In this article, I’ll show you how to handle custom form submissions using the WordPress API. I’ll walk you through the process of adding a custom form in the admin area of a plugin, handle the form submission via an HTML as well as an AJAX request, and write the form handler in PHP to validate, sanitize and process the form input.

While I’ll stay within the admin realms of WordPress, the same concepts are applicable while working with forms in the public facing areas.

I’ll also be making use of object-oriented programming constructs for the plugin; however, you can achieve the same result using procedural code as well. The practice plugin can be downloaded from here to follow along with the article.

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

Let’s get started by first understanding the built-in WordPress mechanism to handle a regular form post request.

Form Submissions with admin-post.php in WordPress

The gamut of hooks available in WordPress gives you great control over the flow of execution of your application. This is no different when it comes to processing forms. All you need is the correct hook to ‘hook into’ and add the custom form handler. The hooks for processing custom forms are dynamic in nature, meaning that the name of the hook partly depends on you.

To process submissions related to your form only, you need finer control as shown below:

WordPress form submission with admin-post.php
WordPress form submission with admin-post.php

This is done by pointing the form submission to the admin-post.php file located in the wp-admin directory of WordPress, and including a custom name for the action in the form. On doing so, WordPress will trigger two action hooks based on the logged in status of the user:

  • admin_post_{$action} for logged in users
  • admin_post_nopriv_{$action} for non-logged in users

Where $action is the name of the action that was passed through the form.

You can then use add_action to tie the PHP form handler to the triggered hooks, where you will have full control to process the form data with the $_GET and $_POST variables.

As you may have guessed already, despite its name, admin-post.php can handle POST and GET requests as well as requests for admin and non-admin areas of the application.

Let’s explore this with the help of a custom plugin.

The Object-Oriented Plugin Structure

My goal here is to help you understand everything that goes behind processing custom forms in WordPress with and without AJAX. For this article, I’ve prepared a custom plugin that you can download from here to follow along. I recommend that you have it open in a suitable editor and install it on a local WordPress setup only.

I built the plugin using object-oriented programming practices with the help of a plugin boilerplate. Boilerplate Starting Points are among the many best practices listed in the WordPress Plugin Handbook. They’re a great way to ensure consistency across your plugins, and save you a lot of time writing standard code. Over a period, you may even end up writing your own custom boilerplate based on your coding preferences. That’s what I did.

The plugin is based on my own plugin template which is a fork of the original WordPress Plugin Boilerplate project. It’s similar to the original project in many aspects but also has support for namespaces and autoloading. This way I don’t need to have unique prefixes for every class or function, and don’t end up with a lot of include and require statements. However, the minimum required PHP version for my plugin is 5.6.0.

Note: If you don’t use namespaces or use procedural code you must prefix everything.

Here’s how the plugin is structured in the backend:

  • inc/core/* – core functionality of the plugin
  • inc/admin/* – functionality related with the admin area
  • inc/frontend/* – functionality related with the public facing areas
  • inc/common/* – functionality shared between the admin and the frontend
oop-based-plugin structure
Plugin structure in the backend

The plugin has a top-level admin menu with two menu items for the form pages.

admin menu structure of the plugin
Admin menu structure of the plugin

To see how I added the admin menu pages, take a look at the define_admin_hooks() method in inc/core/class-init.php and the add_plugin_admin_menu() method in the inc/admin/class-admin.php of the plugin.

If you’d like to know more about adding admin pages to your plugin, have a look at our article about creating WordPress admin pages here.

Adding the Form to the Admin Page of the Plugin

When I added the “HTML Form Submit” menu page for the plugin, I had to also specify the callback to load the page content. This is where the form is added.

However, instead of directly writing the HTML in the html_form_page_content method, I used another file partials-html-form-view.phplocated in inc/admin/views for the form HTML and loaded it in the callback as shown below:

This is purely a coding preference. It allows me to keep my code readable by separating the HTML, and makes no difference to the output of the form on the plugin page.

plugin admin page with html form
HTML Form in the admin page of the plugin

Understanding Form Security, Structure, and Submission

The form that was added above has a select field with a drop-down list of existing WordPress users and two text fields for user input. However, this simple example has a lot going on behind the scenes. The form code below is self-explanatory, so let’s walk through the important elements:

Form Security

The most important thing to keep in mind when dealing with forms in the admin area of WordPress is security. Secure your form using a combination of both WordPress Nonces and current_user_can( $capability ). In my example, I’ve restricted entry to the form with if( current_user_can( 'edit_users' ) ), i.e. the form will be loaded only if the logged in user has the edit_users capability.

I also generated a custom nonce by using wp_create_nonce() and then added it as a hidden form field. You can instead use wp_nonce_field() to add it directly. Here’s a great article to understand Nonces in detail.

Form Structure

I’ve prefixed all form elements with the plugin name to ensure uniqueness. This is again a personal coding preference, as I can be sure of targeting only my form elements through JavaScript. I’ve also used the HTML5 required attribute to leave form validation to the browser.

plugin form inspect view in chrome
Inspecting the admin form

Form Submission

The form submission is made to the admin-post.php using the admin_url( 'admin-post.php' ) function rather than hardcoding the URL. When WordPress receives the form, it will look for the value of the action field to trigger the form hooks. In my case, it will generate the admin_post_nds_form_response hook. Had it been a page open to the public view, it would have triggered the admin_post_nopriv_nds_form_response hook.

The Form Handler for the POST request

At this stage, if you submit the form, you’ll be redirected to an empty page with the page URL set to the admin-post.php. This is because there is no form handler to process the request yet. To process the request, I registered my custom handler the_form_response in the define_admin_hooks() method of class-init.php like this: $this->loader->add_action( 'admin_post_nds_form_response', $plugin_admin, 'the_form_response');

If you were using procedural code you would simply do add_action( 'admin_post_nds_form_response', 'the_form_response');

the_form_response() is where I’ll have full access to the form data via the $_POST or $_GET superglobals. As shown below, I added a breakpoint to the callback in my IDE to be certain that the hook would work as expected.

pausing php script execution
Inspecting form input with XDebug

Form Validation and Input Sanitization

Before performing any operations, you must validate the nonce and sanitize the user input properly. I made use of the wp_verify_nonce( $nonce_name, $nonce_action ) function to verify the nonce, and sanitize_key() and sanitize_text_field() functions to sanitize the user input available in the $_POST variable. If the nonce verification fails, the user will get an error message as the server response, using the wp_die() WordPress function.

Note: I accessed the form data using the $_POST variable. Had I submitted the form using the get method, I would instead make use of the $_GET or $_REQUEST global variable.

Only when I’m sure that everything is in order, would I perform a WordPress operation like adding the user-meta to the selected user.

To know more about input sanitization, I recommend that you read through the WordPress Codex: Validating Sanitizing and Escaping User Data here.

Submitting the Server Response

After performing the server operations, it’s important to send the server response back to the user. To do this, you will first need to redirect the user back to an admin page or one that provides some feedback. I redirected the user back to the plugin page and used WordPress admin notices to display the server feedback. The server response in my example simply outputs the $_POST variable as a WordPress admin notice.

form post server response
Server response from the form handler

Progressive Enhancement

At this stage, I have a fully functional form in the admin area of my WordPress plugin. It’s secure and submits properly to my form handler, where the input data is sanitized and finally, the server response is visible. The form will work out of the box in all browsers that have support for HTML5. But there’s a lot I can do to improve the user experience such as adding AJAX support.

This approach of establishing a basic level of user experience that’s available in all browsers, and then adding advanced functionality for browsers that support it is called Progressive Enhancement.

Note: I’ve made the assumption that my users use modern browsers with HTML5 support. However, if the form had to be rendered on an older browser, the built-in HTML5 input validation for required fields would break. Can I Use is a great website that you can use to compare web features that are available across browsers and browser versions.

Form Submissions with AJAX (admin-ajax.php) in WordPress

AJAX in WordPress is handled via the wp-admin/admin-ajax.php file. Here’s an overview of how custom forms can be processed via AJAX in WordPress:

form support with ajax
Form submission with AJAX support in WordPress

You’ll notice that it’s quite similar to how forms are processed using admin-post.php. When WordPress receives an AJAX request it will create two hooks based on the supplied action:

  • wp_ajax_{$action} for logged in users
  • wp_ajax_nopriv_{$action} for non-logged in users

Where $action is the name of the action that was passed.

Adding AJAX Support to the Plugin Form

The second menu page of the plugin “Ajax Form Submit” loads the form that’s submitted via an AJAX request. It’s added to the menu page in the same manner as discussed earlier, and uses the partials-ajax-form-view.php file to load the form content. If you look at this file, you’ll notice that it’s nearly identical to the earlier form with the only differences being the value of the form id attribute and the title. Now that I can identify one form from the other, I can process just the second form via AJAX using JavaScript.

To add AJAX support, I performed the following steps:

  • Enqueued a JavaScript file to load the jQuery
  • Used jQuery submit event handler to prevent the normal form submission
  • Used jQuery.ajax() to submit the form to admin-ajax.php instead of admin-post.php

Note: If for some reason JavaScript is disabled in the browser, jQuery or AJAX will be unavailable too, but the form will still submit normally. This is because I left the form submission URL as admin-post.php in the form HTML.

Using JavaScript and jQuery to Post the Form

Here’s the JavaScript that I used to submit the form via AJAX.

event.preventDefault(); is what actually prevents the normal form submission.

I gathered the form data using jQuery’s serialize() function but there are many other ways to do this. One of them is using HTML5’s FormData interface. It’s beyond the scope of this article but it’s definitely worth looking at.

var ajax_form_data = $("#nds_add_user_meta_ajax_form").serialize();

I also added additional URL parameters to the serialized data, so I can distinguish between an AJAX and a regular request in the PHP form handler later.

ajax_form_data = ajax_form_data+'&ajaxrequest=true&submit=Submit+Form';

Typically, the X-Requested-With HTTP header is automatically set to XMLHttpRequest by the AJAX library. This can also be used to identify an AJAX request but it’s not always reliable.

The ajax() method of jQuery will submit the request to the server.

To get the form to submit to admin-ajax.php, I used an array params.ajaxurl that was passed in from PHP using wp_localize_script.

Note: The form data in my example includes the action that WordPress will use to generate the hooks for the AJAX request. The following hooks will be triggered by WordPress:

  • wp_ajax_nds_form_response for logged in users
  • wp_ajax_nopriv_nds_form_response for non-logged in users

The JavaScript file is enqueued in the enqueue_scripts() method of class-admin.php as below:

The ajaxurl Global Variable

You can also use a global JavaScript variable ajaxurl instead of passing the URL for admin-ajax.php from PHP. However, the variable is available only when dealing with the admin end and is unavailable when dealing with AJAX on the frontend.

Depending on the response from the server, the AJAX promise callbacks .done() and .fail() will execute accordingly. In my example, for a successful request, I’ve added the response to the empty div container #nds_form_feedback that was part of my form HTML. Finally, the fields are cleared by reseting the form.

The Form Handler for the AJAX Request

I’ve attached the same form handler the_form_response to the AJAX request as well.

And in the form handler, I used $_POST['ajaxrequest'] that was set manually in the JavaScript to distinguish between a normal and AJAX request.

pausing script execution to verify ajax
Validating the AJAX request using a breakpoint

That’s it. With AJAX, the response is displayed without the page being reloaded or redirected.

If JavaScript was disabled or did not load for some reason, $_POST['ajaxrequest'] would not be valid, and the form would submit normally by skipping the AJAX specific if( isset( $_POST['ajaxrequest'] ) && $_POST['ajaxrequest'] === 'true' ) code block.

You can certainly do a lot more to improve the user experience, and I recommend you read through the jQuery API documentation for AJAX here.

Additional Resources

We’ve covered a lot of ground here. AJAX is a fairly vast topic and is implemented in several ways. Here are some more examples of using AJAX in WordPress:

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.

Continue reading, or jump ahead using these links:

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.