Codementor Events

Combining ReactJS and a Wordpress plugin!

Published Apr 16, 2018Last updated Oct 13, 2018
Combining ReactJS and a Wordpress plugin!

Me and the team at Mapster have been building some pretty amazing Wordpress plugins lately. One is a gamified, interactive, complex language LMS -- a system to help students learn an Indigenous language. I've also been building a mapping plugin that integrates a modern mapping API (Mapbox GL JS) into the Wordpress backend. Both have had significant challenges, but one of the more innovative things that has been behind both of them is a deep integration of ReactJS and Wordpress.

You might ask, and rightly so, why React and Wordpress should be together at all. React handles a front end much better than Wordpress's default jQuery stuff, that's certain. And why use all the bells and whistles, and complications, of Wordpress when you could just build out a simple API?

Well, there's a few reasons. Let me explain.

(If you want a quick jump to the end product, check out https://github.com/mapstertech/Wordpress-React-Plugin-Boilerplate)

Using Wordpress

Screen Shot 2018-04-16 at 7.39.40 AM.png

In the case of my mapping plugin, using Wordpress is a no-brainer. This is because that's, well, the whole point of building a Wordpress plugin. There's a big market of users out there (at last count, WP is 33% of the internet, an astounding number). If you want instant access to that many users, build a great, useful, intuitive and well-named plugin. That's the point, so WP is required.

For the Language website plugin, though, I'm deliberately choosing Wordpress for its admin interface and familiarity to users. It comes down to, once again, its popularity. Almost every client has, at one point or another, run into or tried out Wordpress. And if they haven't, the interface is intuitive enough, and there are enough resources out there, that they can figure it out.

Fundamentally, this plugin relies on a lot of complex user input into custom fields, repeatable areas, image and audio uploads, and more. I am using Advanced Custom Fields Pro extensively, and nothing beats it when it comes to making an easy-to-use and robust interface.

While I could absolutely build one of these plugins without Wordpress, it would actually be more difficult because I would need to create a whole complex administrative interface. It's not worth it when we have the front-end to focus on!

Obviously, there are some repos out there already combining React and Wordpress. But I wanted to walk myself through doing it in a different way.

Setting up the plugin

Screen Shot 2018-04-16 at 7.41.31 AM.png

A great place to start with any new WP plugin you're building is Wordpress Plugin Boilerplate. This will help get you structured into an Object-Oriented-Programming frame of mind -- something I learned from one of my sessions here on Codementor with Anton Ukhanev! It is also broken up in a way that separates your front-end and backend, which makes a lot of sense for our uses of ReactJS as well.

Once you've downloaded a copy of the boilerplate and replaced all the necessary items with your own names (see Installation), it's time to set up some React. Sometimes I replace names manually, but you can find commands online to do it in a console.

Now, it's useful here to make a distinction about where we want our React app to live. Do we need a front end display or interface, or something inside the Wordpress admin? Most sites are going to do the first, but backend plugins would do the second, since they mess around more in the admin area of Wordpress, adding new settings pages and post types.

Let's cd into the folder where we're going to have our React app -- either public/ or admin/, depending on what you decided -- and create-react-app my-app-name. Make sure Create React App is installed first, of course. It'll create some basic folders for you and now you have a React app living inside your WP plugin. npm start and then close the browser window that appears -- we won't be developing at http://localhost:3000 this time!

We need to do some preliminary stuff to prep our Wordpress install and allow React to connect and play nicely. This is most important for the front-end, because you will probably want to access Wordpress's WP JSON API to get access to data. You can specify which posts or post types are available, and if you create a custom post type you'll have to add it manually to REST (a bit of a process).

In this case, we're going to create a page to serve up our React app on the public side. Here's some code that auto-generates that page on plugin init:

      $my_page = get_option('plugin_name_page');
      if (!$my_page||FALSE === get_post_status( $my_page )) {
          // Create post/page object
          $my_new_page = array(
              'post_title' => 'CRA App Page',
              'post_content' => '',
              'post_status' => 'publish',
              'post_type' => 'page'
          );
          // Insert the post into the database
          $my_page = wp_insert_post( $my_new_page );
          update_option('plugin_name_page',$my_page);
      }

This also saves the page ID into an option, so we'll be able to load scripts conditionally later. We also add a bit of code in the public side to ensure this page actually loads the right template -- which we'll create next.

Enqueuing for prod and dev

preview.jpg

If we're building an admin app, then we need to create some kind of page where the app is going to run. My admin app runs from a custom post type, and inside a metabox -- but yours could also just be on a settings page. You need to create a place where you'll be able to direct your React scripts to load.

In this case, since most people will be doing public-facing apps, we'll just go through that process. If you follow it, you should be able to extrapolate for the admin version of the same thing.

Here's my highly simple page template:

<?php
/**
 * Plugin Name page template (for React app)
 *
 * @since    1.0.0
 */ ?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
 <head>
 	<meta charset="<?php bloginfo( 'charset' ); ?>">
 	<meta http-equiv="X-UA-Compatible" content="IE=edge">
 	<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
 	<meta name="mobile-web-app-capable" content="yes">
 	<meta name="apple-mobile-web-app-capable" content="yes">
 	<meta name="apple-mobile-web-app-title" content="<?php bloginfo( 'name' ); ?> - <?php bloginfo( 'description' ); ?>">
 	<link rel="profile" href="http://gmpg.org/xfn/11">
 	<link rel="pingback" href="<?php bloginfo( 'pingback_url' ); ?>">
    <link href="https://fonts.googleapis.com/css?family=Fredoka+One" rel="stylesheet">
    <link href="https://use.fontawesome.com/releases/v5.0.6/css/all.css" rel="stylesheet">
 	<?php wp_head(); ?>

 </head>

 <body <?php body_class(); ?>>
   <input id="home_url" type="hidden" value="<?php echo get_home_url(); ?>" />
   <div id="root"></div>
 </body>

<?php get_footer(); ?>

Now let's enqueue styles and scripts conditionally, depending whether we're on build or dev, and make sure that Wordpress always grabs the latest file.

Styles:

    $my_page = get_option('plugin_name_page');
    if($my_page && is_page($my_page)) {
      if (!in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1'))) {
        $CSSfiles = scandir(dirname(__FILE__) . '/create-react-app-name/build/static/css/');
       	foreach($CSSfiles as $filename) {
          if(strpos($filename,'.css')&&!strpos($filename,'.css.map')) {
            wp_enqueue_style( 'plugin_name_react_css', plugin_dir_url( __FILE__ ) . 'create-react-app-name/build/static/css/' . $filename, array(), mt_rand(10,1000), 'all' );
          }
       	}
      }
    } else {
      wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/plugin-name-public.css', array(), mt_rand(10,1000), 'all' );
    }

Scripts:

    $my_page = get_option('plugin_name_page');
    if($my_page && is_page($my_page)) {
      if (!in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1'))) {
      	// code for localhost here
        // PROD
       	$JSfiles = scandir(dirname(__FILE__) . '/create-react-app-name/build/static/js/');
       	$react_js_to_load = '';
       	foreach($JSfiles as $filename) {
       		if(strpos($filename,'.js')&&!strpos($filename,'.js.map')) {
       			$react_js_to_load = plugin_dir_url( __FILE__ ) . 'create-react-app-name/build/static/js/' . $filename;
       		}
       	}
      } else {
        $react_js_to_load = 'http://localhost:3000/static/js/bundle.js';
      }
     	// DEV
     	// React dynamic loading
     	wp_enqueue_script('plugin_name_react', $react_js_to_load, '', mt_rand(10,1000), true);
     	// wp_register_script('plugin_name_react', $react_js_to_load, '', mt_rand(10,1000), true);
      //
      // wp_localize_script('plugin_name_react', 'params', array(
      //     'nonce' => wp_create_nonce('wp_rest'),
      //     'nonce_verify' => wp_verify_nonce($_REQUEST['X-WP-Nonce'], 'wp_rest')
      // ));
      // wp_enqueue_script( 'plugin_name_react' );
    } else {
      wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/plugin-name-public.js', array( 'jquery' ), mt_rand(10,1000), false );
    }

So what does this code do? You need to understand it, because it might not be perfect for your use case. Knowing it, you can modify accordingly.

  1. Both scripts first check to see if we're on the page we want to load React on.
  2. We check if we are at a localhost URL. This is a quick and dirty way to know that we are not on the production website.
  3. If it's production, scan the build/ directory and select the CSS or JS file. This file always has a random name in CRA, so we scan for it instead of specifying the exact filename.
  4. If it's dev (localhost), then get the bundle generated from CRA when npm start is run. Load the script!
  5. If we're not on the React page, load whatever normal custom WP CSS is needed.

Substitute in whatever you call your app for create-react-app-name/. I've also included some commented code that should help you get started with nonces in your API interaction, too.

Run npm start in the app directory, and head to the auto-generated page with your page template loading. Any errors? Let me know if you have any! If not, at this point, you should be able to see the CRA app loading up inside a root div on whatever screen you set up, settings or a page template. Nice work!

Some warnings

This app is still running in Wordpress -- that means that jQuery might be loaded, or plugin scripts, or other crazy things. Make use of deenqueue_scripts to remove any trouble CSS or JS. Otherwise this will drive you wild!

Continue to use unique class names as you would in any Wordpress plugin. Just because the CRA gets bundled and built doesn't mean it won't be easily broken by default CSS!

Use hidden inputs and other techniques to get the URLs, IDs, or other basiic information required from the WP database. Otherwise, you'll be making absolute calls and things will be broken between local and prod.

Overall, despite the problems, I've found this setup to be easy to use and relatively dependable with CRA, and it's very easy to upload new versions of your app -- just delete the old build/ folder and upload a new one.

Conclusion

Screen Shot 2018-04-16 at 7.44.29 AM.png

Check out the repo and make some suggestions to fix any bugs at https://github.com/mapstertech/Wordpress-React-Plugin-Boilerplate.

I hope this helped you get rolling fast with ReactJS and Wordpress, and maybe it'll help you developing your next unique and amazing plugin. I'll help if you need more, just send me a note!

Enjoy! Our team at Mapster is finishing up Learn A Language and WP Mapbox GL JS, which both employ some variation of React and Wordpress integration. These are nearing a first release date, and we look forward to sharing them with you soon!

Discover and read more posts from Victor Gerard Temprano
get started
post commentsBe the first to share your opinion
skunkbad
5 years ago

It seems like the most recent version of create-react-app has changed the files that are built when one runs “npm run build”. So your code wasn’t working for me. There are now 3 JS files. I’ve got some hard coded paths in the snippet I’m sharing, but take a look at it if you need help making this work with the most recent React:

https://bitbucket.org/snippets/skunkbad/LexbgB

Jake Greer
5 years ago

I’m not understanding this to its full extent, after cloning the project listed here https://github.com/mapstertech/Wordpress-React-Plugin-Boilerplate, I navigated to the public folder and ran create-react-app. I went into the newly created app folder and ran build. I then went to class-plugin-name-public.php and pointed the enqueue styles and scripts lines to my create-react-app build folder. I assumed after doing this I could pull up a basic CRA as a plugin. The problem I am having is I am not sure how to install this to WordPress. I tried to compress the folder into a zip and install it through the WordPress admin but this fails and says no plugin files detected. I understand that I am probably going about this the wrong way. Any help on this would be greatly appreciated!

Jake Greer
5 years ago

I realized that I was compressing the parent folder and not the actual project folder itself my mistake! Although this raises a new question how would I go about adding this to the sidebar on the admin menu? Sorry noobie here.

Peter VanKoughnett
5 years ago

Any advice on getting the files to have version numbers? My CSS files are getting loaded without version numbers and this is causing browser caching problems

Victor Gerard Temprano
5 years ago

I sometimes use something random like mt_rand(1,1000) so it just adds a random number on the end!

Peter VanKoughnett
5 years ago

Yes I saw that in your code, but my problem is that I wasn’t getting a version number at all. It seemed to have to do with the interaction between WordPress and the source mapping file. I ended up resolving this by deleting the source map files after building. You can automate this by adding “postbuild”: “rimraf build/**/*.map” to your package.json scripts

Show more replies