Deploying a React App to Netlify

As part of the creating a headless React-based frontend for my WordPress site, I decided to try hosting my files on Netlify. They tout being super fast when serving a non-database site; a natural fit for a React project. Wes Bos’s React for Beginners course shows how to deploy to Netlify using the netlify-cli npm package. However, since completing that course, I updated to Node 10, which is not compatible with netlify-cli and Netlify recently deprecated it. In this post, I’ll detail how to deploy a site from the command line using their new command line tool: netlifyctl.

Sign Up for Netlify

If you don’t already have an account, head over to and sign up for an account. You shouldn’t need to create a paid account yet, so don’t worry about adding payment options.


First off, you’ll need to install the new tool on your computer. On OS X, it’s available through Homebrew and on Windows, it’s available through Scoop.


If you don’t already have Homebrew installed, open Terminal and enter this command, then follow the prompts:

/usr/bin/ruby -e "$(curl -fsSL"

With Homebrew installed, point to the netlifyctl tap:

brew tap netlify/netlifyctl

Next, install it:

brew install netlifyctl


If you don’t already have Scoop installed, make sure you have PowerShell 3 or higher installed, open it, then install Scoop using:

iex (new-object net.webclient).downloadstring('')

With Scoop installed, add the netlifyctl bucket:

scoop bucket add netlifyctl

Then install it:

scoop install netlifyctl

Deploying a React App

In this case, I’m specifically talking about a project created with create-react-app. If you aren’t using create-react-app, you will need to change some paths to match your project.

With netlifyctl installed, change directories into your project:

cd /myreactapp


Start by giving your app permission to access your Netlify account.

netlifyctl login

This will openyour Netlify account in a web browser and ask if you want to give the CLI permission to access your account.

Netlify grant permissions prompt

Click the Authorize button. Once it confirms the CLI has permission to access your account, you can close the browser tab/window.


If you haven’t already, run the build process for your app. For create-reat-app, that’s:

npm run build


Next, use this command to start the deploy process to Netlify:

netlify deploy

It begins by asking if this is a new site. Entering “no” allows you to search by name for the existing site. Entering “yes” creates a new site.

Either way, it then asks for the path you’d like deployed. For a create-react-app project, it is:


This uploads your build directory to Netlify and prompts you with the URL for your deployed app, which you can alt+click to view.

Last Things

That’s it! Your site should be visible on Netlify! A few last things: you’ll notice there’s now a netlify.toml file in your project. This file contains the settings you selected while deploying your app. Commit that file to your git repo to avoid answering those prompt again in the future.


If you forget how all this works, you can use:

netlifyctl --help

This lists out the commands available and how to use them. You can also use it with a specific command like:

netlifyctl deploy --help

Bonus Material

If you need to make changes to your app – say you’re like me and you forgot to run “npm run build” first so you deployed the default create-react-app build folder – and you need to re-deploy the project. Fortunately, it’s very simple:

netlify deploy

However, each time, the script prompts for the build path. This”feature” grows tiresome when you forget another thing, then another thing, then another thing and perform several deployments in a row (not that I would know from experience…). To avoid this, edit the netlify.toml file and add the build path. The default file includes your Netlify site ID and:

  Publish = ""
  Functions = ""

Add your build path in the Publish value between the double quotes:

  Publish = "./build"

Now, the deployment script uploads the files from the build folder and doesn’t stop for prompts each time.

Troubleshooting WordPress AJAX

I was working with some AJAX functionality a while back and started writing down the problems and solutions I ran across so they were all in one place. Basically, this post is for my future self. I hope you find it useful as well. 🙂

Specific Problems

Function Always Returns 0

If you’re getting a “0” as the response from an AJAX call in the admin, it’s most likely one of three things:

  • WordPress can’t find the function you’ve hooked.
  • You don’t have a wp_die() at the end of the PHP function.
  • The action isn’t in the data you’re sending to the PHP function.

403 Forbidden Error

Check the Nonce. You probably copied it from another function and it’s checking the wrong one.

Helpful Tip

The PHP function that processes the AJAX request has to echo something and should end with a wp_die() statement. You can use wp_die() to return something you want to see, like what data was passed to the function.

wp_die( print_r( $some_array ) ) is useful to check an array.

PHPUnit tests and WP-CLI

I recently discovered an easy way to add unit tests to my plugins. I’ve been using WP-CLI for years now to set up new local installs of WordPress and found out it has a command specifically for setting up PHPUnit for plugins. This post is partially for my future self so I can remember how to do all this. FYI, I’m on a Mac, so all the commands will be OS X-specific.

Install and Configure PHPUnit

Start by installing PHPUnit using Homebrew:

brew install phpunit

That installs the latest version of PHPUnit and makes it ready to use with the global keyword “phpunit”.

Next, use WP-CLI to setup the test suites and such needed for PHPUnit. I recommend running this command from the plugin’s directory.

wp scaffold plugin-tests my-plugin

Replace the “my-plugin” part with your plugin name.

If you aren’t already in your plugin directory, change to that now. Then you’ll run the installation bash script.

bin/ wordpress_test root '' localhost latest

Notes about this command:

  • ‘wordpress_test’ is the MySQL database created by this script and used for testing
  • ‘root’ is your local MySQL user
  • ” is where your local MySQL user password goes
  • ‘localhost’ is where you MySQL server is located
  • ‘latest’ changes the version of WordPress to use for testing.

Once you’ve run this script, you’re ready to start writing tests and running them using the ‘phpunit’ command. Here’s the rub though: the script installs WordPress and the testing suite in the /tmp directory. Why is that a  problem? On my computer, at least, the /tmp directory removes anything installed there upon reboot. Which means I need to re-run the tests bash script every time I reboot the computer.

Since we developers prefer not repeating ourselves, here’s how to configure things for a more permanent solution.


What I’m advising here is to move the stuff installed in /tmp to somewhere else, then fix all the references so they still work. So, run all of the above, then navigate to your /tmp folder. Move the /wordpress and /wordpress-tests-lib folders to another location outside the /tmp folder. I moved mine into a directory called unittests in my Dropbox folder.

Go to your Home folder and either open or create the .bash_profile file. You’ll probably need to tell Finder to show Hidden files first. In your .bash_profile file, add the following line, using your new directory location:

export WP_TESTS_DIR="/Your/New/Folder/Location/unittests/wordpress-tests-lib"

This line tells the PHPUnit bootstrap.php file where to find the test WordPress install and test suite files.

Now, go to the wordpress-tests-lib directory and open the wp-tests-config.php file. Edit the ABSPATH constant to the ‘/wordpress’ directory you copied over from /tmp earlier. Something like:

define( 'ABSPATH', '/Your/New/Folder/Location/unittests/wordpress//' );

Save the file and reboot your computer. Now, the tests setup by WP-CLI know where the testing WordPress is located, it doesn’t disappear when you install updates or reboot, and you can create and run PHPUnit tests for your plugins!

How to Change the Featured Image Labels

When creating a site for a client or creating a plugin, I’ve found its helpful to customize things as much as possible to the intended usage. This is especially important for client work since most clients have specific terminology they use for things. In the case of Featured Images, the site or plugin might be using the featured image differently than how one might use it on a news or blog post.

I’m currently writing a plugin with a custom post type and using the featured image as a headshot for an employee. While “featured image” may work fine, “headshot” is more specific and makes more sense in this context. I haven’t been able to find anything recent about how to change the labels on the existing Featured Image metabox. The most commonly referenced code only works some of the time. Specifically, when one removes a featured image, the label for the link changes back to referencing “featured image” instead of the customized label.

I dug through the core and found the post_type_labels_{$post_type} filter, which was added in version 3.5. This filter makes customizing the featured image labels super easy:

 * Changes strings referencing Featured Images for a post type
 * In this example, the post type in the filter name is "employee" 
 * and the new reference in the labels is "headshot".
 * @see
 * @param     object    $labels    Current post type labels
 * @return    object               Modified post type labels
function change_featured_image_labels( $labels ) {

    $labels->featured_image 	= 'Headshot';
    $labels->set_featured_image 	= 'Set headshot';
    $labels->remove_featured_image 	= 'Remove headshot';
    $labels->use_featured_image 	= 'Use as headshot';

    return $labels;

} // change_featured_image_labels()

add_filter( 'post_type_labels_employee', 'change_featured_image_labels', 10, 1 );

Gist of the code above

The labels are in an object and we then reset the values of each specific item to what we want. Then return the object.

Child themes for WooThemes

I’ve been using child themes for several years now, but I’d never used one with a theme by WooThemes. Most of my clients needed simple customizing, so I used WooTheme’s backend options to make the changes.

A recent client needed a bit more than I could do through their backend though. I started off making the customization directly in the theme, which is never a good idea. Catching my mistake, I decided to throw my changes in a child theme and work from there. However, when I reloaded after activating the child theme, the site broke. Looking at the source code, I noticed the order of the stylesheets. My child theme stylesheet should be last in the cascade, therefore overriding all other styles. There were two files from the parent theme loading after mine. I tried several solutions, hoping a newer one using wp_enqueue_style to load the parent stylesheet instead of @import would work, but that didn’t affect the other two stylesheets.

Thankfully (before pulling my hair out), I found this page in the WooThemes support docs. What they don’t spell out on the page is that you don’t actually make CSS changes in the child theme style.css. You make a second CSS file called custom.css and make your changes there. The custom.css file loads next-to-last followed by styles put into their custom CSS field on the backend forms and therefore overrides all the other stylesheets.

Option Saving Troubleshooting

WARNING: In this article, I ask you to alter core WordPress files while troubleshooting. Please remember to undo any changes you make so WordPress continues working correctly. I do NOT advise making these changes on a public-facing website.

Recently, I’ve been troubleshooting why my options for a new plugin aren’t saving correctly. So far, I’ve discovered or should I say “re-discovered” some interesting items that other devs might find useful. For starters, when saving your plugin options, ideally, you should combine them into an array before saving them. This saves the database from having tons of random options entries just for your plugin. There are exceptions, like if you need options separated from each other, but that’s another post.

When saving data into an array, you’ll need to make the name of the field something like:


Notice those brackets? That helps the options saving process place this field’s data into the correct place within the array.

While troubleshooting options, I began to trace where the data goes and how it gets processed by WordPress, then your own plugin, so I’m going to lay it out here.

Saving From the Settings Page

First, when you create your settings page, your form tag has these attributes:

method="post" action="options.php"

This means the form will use the Post method to send the data to the options.php file.

The action check in wp-admin > options.php.
The action check and the added wp_die() in wp-admin > options.php.

Within the options.php file (version 3.9.1 as of this writing), line 133 checks if the action was “update”. There’s lots of code before this mostly dealing with making sure WordPress will be saving to the correct set of options, which changes if you’re saving options for Multi-site vs a single site installation.

If one checks the $_POST PHP superglobal on line 134, you’ll see the raw posted information you typed into the fields. To do this, one would use this bit of code within the root > wp-admin > options.php file:

wp_die( serialize( $_POST ) );

wp_die() stops WordPress right where it is and the first argument allows you to enter an optional message. In this case, we’re serializing the $_POST array so we can read it and using that as our message. From my test data, here’s what I see:

a:6:{s:11:"option_page";s:19:"slushman_wpmi_first";s:6:"action";s:6:"update";s:8:"_wpnonce";s:10:"4d37626dc4";s:16:"_wp_http_referer";s:77:"/showcase/wp-admin/options-general.php?page=wp-maximage&settings-updated=true";s:19:"slushman_wpmi_first";a:2:{s:15:"wpmi_test_field";s:9:"save this";s:17:"wpmi_test_field_2";s:0:"";}s:6:"submit";s:12:"Save Changes";}

In my test settings page, I have a field called  ‘slushman_wpmi_first’ and I entered ‘save this’ and clicked the Save Changes button.

After checking if the action is “update”, options.php then checks the admin referrer, which in our test case is ‘/showcase/wp-admin/options-general.php?page=wp-maximage&settings-updated=true‘. You can comment out and copy the wp_die() statement then paste it  just after this admin referrer check, but it should be the same stuff.

After this, it checks the whitelisted options. Most of the code above the update check in line 133 is about compiling that list of whitelisted options. If you used the register_setting() function for your plugin options, your options should be automatically added to the whitelisted options. It then checks your capabilities for multisite installations or whitelists your settings. If you’re not having capabilities issues, copy/pasting the wp_die() statement after line 153 really shouldn’t show anything different.

The next batch of code has to do with checking the time and date settings from the WordPress general page, so skip that block of code.

Line 168 checks if the variable $options is valid and has data. If all the whitelisting code passed, this variable should have been assigned in line 151. You can move the wp_die() statement to line 169 and change $_POST to $options to see your whitelisted options. Here’s what my test settings page shows:

The option value processing block in wp-admin > options.php.
The option value processing block in wp-admin > options.php.

This is the first bit where the data starts to be processed and possibly changed. After checking the $options variable, it breaks each option apart and processed them separately (in PHP, that’s a foreach loop). If you copy/paste the wp_die() within the foreach loop and change $options to $option, you should see the first one:


First, it checks each option to see if it’s unregistered, which was assigned to the $unregistered variable back in lines 135 or 138. Since you’ve used the register_setting() function, this should be false because your setting is registered, otherwise, you’d see a WordPress error once the processing reaches this point.

The option is trimmed to remove extra white space and the $value variable set to NULL in lines 172 and 173.

Line 175 checks the $_POST superglobal for the option. If it’s not in $_POST there’s no reason to attempt to update it, so it would skip any further processing and go back to the settings page.

Otherwise, in line 175, it assigns the $value variable the value of the option from $_POST. You can check this value for yourself by using this on line 176:

wp_die( serialize( $_POST['name_of_your_option'] ) );

In my test page, I get:

a:2:{s:15:"wpmi_test_field";s:9:"save this";s:17:"wpmi_test_field_2";s:0:"";}

It seems that both fields were submitted for the option ‘slushman_wpmi_first’. In the next line, it check the data type of the $value and if it’s not an array, it trims it to remove white space, the sends it off to wp_unslash() to remove any slashes.

After this, the $option and the $value are sent to update_option(). One might think the processing ends there, but no. update_option() is located in the wp-includes/option.php file around line 231. Pasting this at the top of the update_option() function:

wp_die( serialize( $value ) );

Yields this result for my test page:

a:2:{s:15:"wpmi_test_field";s:0:"save this";s:17:"wpmi_test_field_2";s:0:"";}

First, it trims $option again. If $option is empty, it returns FALSE. Then, it sends $option off to wp_protect_special_option(), which is around line 136 in this same file. It prevents you from updating two options used by WordPress (since WordPress 2.2): alloptions and notoptions. These are used by WordPress in conjunction with transients to autoload options. I’d welcome any additional comments about their roles.

Next, if the $value is an object, it creates a clone of the object and works that with the clone rather than the original.

It then sends $option and $value off to sanitize_option(), which is in the wp-includes/formatting.php file. Looking at the code for sanitize_option(), most of it consists of a switch statement that looks at the $option parameter and acts accordingly. It already includes processing instructions for all the standard WordPress options, but after the switch statement, it includes a filter, ‘sanitize_option_{$option}‘. The sanitize_option_ filter is created and defined in register_setting() (yet another reason to use the Settings API). When you define your sanitization callback function, it adds that function to the sanitize_option_ filter.

At this point, your option’s $value is filtered by whatever you put into your sanitization callback function. After it finishes processing in your sanitization callback, its then returned back to the update_option function. If you add this to the top of your sanitization function:

wp_die( serialize( $input ) );

You can now see exactly what’s being passed into your sanitization callback function. In my case:

a:2:{s:15:"wpmi_test_field";s:9:"save this";s:17:"wpmi_test_field_2";s:0:"";}

After the new value is sanitized in your callback function, the current “old” value is recovered from the database using get_option().

The new value is now passed through two more optional filters, ‘pre_update_option_’ . $option‘, and ‘pre_update_option‘.

Now the new and old values are compared. If they aren’t different, there’s no reason to update the value, so it exits. If the old value doesn’t exist, The new value is sent off to add_option() to be created, the result is returned to this function as $value.

The $value is now sent off to maybe_serialize(), which is in the wp-includes/functions.php file, to possibly serialize it, or in other words, prep it to be stored in the database safely.

Updating the database in option.php
Updating the database in option.php

After this, an action is added for ‘update_option‘. One might get this mixed up with the function update_option(). The action happens right before the option is actually updated, while the function does all the checks and processing to make the updated data safe.

Finally, the updated value is written to the database using the $wpdb object and its update() method. The update() method does not alter the data.

After saving the data to the database, it refreshes the options caches and added to the auto-loading process.

There are two final actions, which allow for processing the value after its been saved to the database: ‘update_option_{$option}‘, which happens after a specific value has been updated, and ‘updated_option’, which happens after the value an option has been updated.

Lastly, if all has gone to plan, update_option() return TRUE.

Back in options.php, if errors occurred at some point in this process, they are handled and displayed appropriately.

Then, finally, the user is sent back to the plugin’s settings page.

Obviously, there are lots of places for things to go badly when saving your plugin settings. Hopefully, this breakdown can help narrow down where things happen and you can use the wp_die() function to discover where issues are happening. In all cases, I’d highly recommend using the Settings API as much as possible!

Subversion, Coda, and WordPress

I’ve been using Panic’s Coda to develop my WordPress plugins and manage my sites and I love it. When I published my first plugin, it took me quite a while to find any real resources for using Coda with WordPress’s Subversion servers. It’s fairly simple stuff really, but updating plugins baffled me for the longest time. I’ve finally figured out the proper steps to do it without errors or partial uploads. As evidenced by the version 0.32 of ArtistDataPress plugin that didn’t include the widget, CSS, or images folders…

Developer Tip

When using Subversion for source control, use the trunk files. These files are solely for developers, not the public. If you’re working with other developers, they check out this brand to work from.

Here are my tested instructions for using Coda to update a WordPress plugin:

Make changes as needed to your plugin files. Be sure to update the stable version number in your main plugin and readme files.

Go into the tags folder. Duplicate the latest tag folder in there. When it asks if you want to update, say yes.

Change the name of the folder to the latest version number. Say yes to updating.

Copy the updated files from the trunk folder into the newly-created tag folder. At this point, there should be a green A next to that tag folder.

After copying all the files to the tag folder, click the green A. This adds the folder to the SVN repository. Coda then asks you to type in notes about the changes. Make them as detailed as possible. These notes are how people know what changed in the code. Your future self and other developers will thank you.

At this point, the new tag folder with all the updated files are in the SVN repository. Wait about fifteen minutes and you should see the changes reflected on the plugin’s page in the WordPress plugin directory.

If the steps above don’t work or you start getting odd errors (like you need to force an update), copy your trunk files to your desktop and remove the site. Then add the site back and check out the repository again. Then follow the steps above. I just ran into this today (2/11/2012) and this process worked for me.