Why and how to build WordPress settings pages with React

As a WordPress developer, creating settings pages in the dashboard is often necessary to enhance the admin experience. Although these pages aren’t publicly visible, ensuring a smooth and efficient admin interface is crucial.

Traditionally, WordPress provides many libraries, such as jQuery and jQuery UI, to build these pages. However, the introduction of the Gutenberg editor in WordPress 5.0 marked a significant shift. The Gutenberg editor, based on React, has revolutionized content creation with its block-based approach.

This shift is not limited to content creation. React components are becoming increasingly prevalent throughout the WordPress dashboard, including settings pages. This article will explore why you should build settings pages with React and how to do it effectively.

Before starting, I would like to mention that WordPress shares its components library on Storybook where we as developers can explore components, check each component’s properties, see how it looks, and get a sample code. Here I am sharing how the DropDownMenu component looks there. Click here to access that library

Creating the admin page

I am assuming that we are going to create the settings page from a plugin, and usually, I like the WordPress plugin boilerplate provided by https://wppb.me/ so feel free to go there and generate yours. I created one and will call it “wp-react-settings-sample”. Here is how it looks in vs code

Adding a setting page in WordPress requires using the function add_menu_page in a callback function of the hook admin_menu. Therefore, I am opening the file includes/class-wp-react-settings-sample.php and inside the function define_admin_hooks I will add this line:

includes/class-wp-react-settings-sample.php
$this->loader->add_action('admin_menu', $plugin_admin, 'define_settings_page');

After that, we should define the define_settings_page function, this will be in the admin/class-wp-react-settings-sample-admin.php file

admin/class-wp-react-settings-sample-admin.php
public function define_settings_page()
{
	add_menu_page(
		__('Page Title', 'wp-react-settings-sample'),
		__('Menu Title', 'wp-react-settings-sample'),
		'manage_options',
		'wp-react-settings-sample',
		array($this, 'settings_page'),
		'dashicons-admin-generic',
		6
	);
}

please refer to WordPress Documentation if you need to learn more about the add_menu_page function

The 5th parameter of the function add_menu_page is mentioning the callback function that will be used by WordPress to define the page content. In our case, it is array($this, 'settings_page') which means that it is a function called settings_page defined in the same class, so let us define it:

admin/class-wp-react-settings-sample-admin.php
public function settings_page()
{
	printf(
            '<div class="wrap" id="wp-react-settings-sample-root">%s</div>',
            esc_html__('Loading…', 'wp-react-settings-sample')
        );
}

The div that the function settings_page is printing has the class wrap used as a wrapper in all WordPress pages and the id wp-react-settings-sample-root which will be used by React later to render the components.

If everything is fine, you should see the new page in the dashboard with this content

The React Setup

The React setup in this context is a bit different than starting a regular React project because WordPress already includes React and many other libraries under something called WordPress Scripts or wp-scripts.

WordPress Scripts is your starting point when developing a custom block, widget, settings page, or theme that follows the full-site editing rules. So far, there are more than 95 packages and React is just one of them. a full list of the packages can be found here.

Anyway, for our situation, we will start by executing this command in the root folder of our plugin

npm install @wordpress/scripts@27 --save-dev

This command will install version 27 of WordPress scripts. However, at the time of writing this post, the latest version of wp scripts is 28.2.0 but it requires the WordPress version to be 6.6 at least which is still not public yet.

You will also find the package.json file created automatically and node_modules folder where the package and all its dependencies are installed.

Additionally, I will create the file src/index.js. So there will be a folder called src and index.js inside it. Usually, the src folder is the default source code directory for wp scripts, and there is no need to customize it in our case.

I will modify the package.json file to have some commands that we will need to execute later:

package.json
{
  "devDependencies": {
    "@wordpress/scripts": "^27.9.0"
  },
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start"
  }
}

Briefly, here is how the process will go:

  • The command “start” we have just added to the package.json will start the node server.
  • The server will compile the code in src/index.js and generate a bunch of files that we will use.

With this, let us modify the src/index.js and add the following code:

src/index.js
import domReady from '@wordpress/dom-ready';
import { createRoot } from "@wordpress/element";
domReady(function () {
    const root = createRoot(
        document.getElementById("wp-react-settings-sample-root")
    );
    root.render(<>Rendered Correctly</>);
});

Notice that we are using 2 packages in index.js

@wordpress/dom-ready enables us to execute callback after the DOM is loaded.

@wordpress/element is a package that builds on top of React and provides a set of utilities to work with React components and React elements.

Running the command npm start will generate a folder called build which has 3 files:

index.js: It is the final js file that we will later include in the WordPress dashboard, so it will be shipped to production

index.asset.php: In my case, here is the content of the file

build/index.asset.php
<?php return array('dependencies' => array('react', 'wp-dom-ready', 'wp-element'), 'version' => 'e049722bc0e5f9da3c41');

The content of the dependencies array is dynamic and it has the equivalent libraries of the WordPress packages we used in index.js. So when we include index.js in the dashboard, we do not have to worry about which dependencies it has since it is already generated for us. The version key is dynamically generated as well and we will use it when we include the script file.

index.js.map: is the map file of index.js, it is there just because we used the npm start command and it is not a part of the final build. Here is how it looks on my device:

So, let us include the index.js in our dashboard. This will be done in admin/class-wp-react-settings-sample-admin.php specifically in the function enqueue_scripts

However, we will modify the function header to get the $hook_suffix parameter which informs the current admin page.

admin/class-wp-react-settings-sample-admin.php
public function enqueue_scripts($hook_suffix)

In our case, the value of this parameter is toplevel_page_wp-react-settings-sample where the part toplevel_page_ is automatically added by WordPress to all top-level pages, and wp-react-settings-sample is the menu slug that we defined in the add_menu_page function.

admin/class-wp-react-settings-sample-admin.php
// checking if we are in the required settings page
if ($hook_suffix === 'toplevel_page_wp-react-settings-sample') {
	// get the php asset file path
	$asset_file = plugin_dir_path(__DIR__) . 'build/index.asset.php';
	// if that file is generated correctly, include it
	if (file_exists($asset_file)) {
		// the returned array from that file will be stored in the variable $asset
		$asset = include $asset_file;
		wp_enqueue_script(
			// script handle
			'wp-react-settings-sample-admin-script',
			// the index.js file url
			plugin_dir_url(__DIR__) . 'build/index.js',
			// the dynamic dependencies from index.asset.php
			$asset['dependencies'],
			// the dynamic version from index.asset.php
			$asset['version'],
			array('in_footer' => true)
		);
	}
}

Now, this should include the index.js in our settings page, with the code attached in index.js, the result that we should see is having <>Rendered Correctly</> rendered in the div#wp-react-settings-sample-root.

Now under the src folder, you can create your components folder and start building your components that you want to see on the settings page. Just for testing purposes, I will create the src/components/bg-color-change.js component with the following content:

src/components/bg-change-color.js
import { useState } from 'react';
import { ColorPicker } from '@wordpress/components';
const BgColorChange = () => {
    const [color, setColor] = useState();
    return (
        <>
            <h2 style={{backgroundColor: color}}>Change Background Color</h2>
            <ColorPicker
                color={color}
                onChange={setColor}
                enableAlpha
                defaultValue="#000"
            />
        </>
    );
}
export default BgColorChange;

Then update index.js to render that component

src/index.js
import domReady from '@wordpress/dom-ready';
import { createRoot } from "@wordpress/element";
import BgColorChange from './components/bg-color-change';
domReady(function () {
    const root = createRoot(
        document.getElementById("wp-react-settings-sample-root")
    );
    root.render(<BgColorChange />);
});

Tada!

This is the end for now. I am preparing another tutorial where we can use more components from WordPress components and work with real WordPress data. There, we will see practically the benefits of using React for building WordPress settings pages. Stay tuned!

Leave a Reply

Your email address will not be published. Required fields are marked *