Integrate React into a WordPress project

Integrate React into a WordPress project

ยท

4 min read

Same platform (WordPress ๐Ÿ’™), different projects, new challenges, new problems, new solutions! On the other hand, this is the work of us developers. ๐Ÿ˜Ž

Recently, for a client who works in the Agro-Food sector, I was asked to set up a configurator for physical containers. Without going into too much detail of the project, I leave a reference link for those who want to see it in action.

The idea was to provide the end user with a browsing experience without page refresh and given the trends of the moment in the front-end development and considering that React is already used in Wordpress for the Gutenberg editor, the choice was quite immediate!

For this guide I refer to a start-theme that I wrote and use as a template for custom projects on WordPress.

Theme sctructure

I will only put the files that interest us, to see the entire architecture click here!

theme-name/
  โ””โ”€โ”€ package.json
  โ””โ”€โ”€ webpack.config.js
  โ””โ”€โ”€ page-templates/
  โ”‚     โ””โ”€โ”€ my-component-template.php
  โ””โ”€โ”€ public/
  โ””โ”€โ”€ src/
      โ””โ”€โ”€ app/
          โ””โ”€โ”€ my-component/
              โ””โ”€โ”€ App.jsx
              โ””โ”€โ”€ my-component.js
              โ””โ”€โ”€ my-component.scss

Webpack configuration

I won't go too far into explaining this file (it would almost take a separate article ๐Ÿ˜…) except for the fact that I have used the browser-sync-webpack-plugin plugin which I highly recommend when we need to update the UI often.

const path = require('path');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const envConf = require('./config');

const entryPoint = {
    index: './src/app/index.js',
    about: './src/app/about/about.js',
    globalCss: './src/app/global.scss',
    myComponent: './src/app/my-component/my-component.js'
}

const pathResolve = path.resolve(__dirname, './public');

const moduleRules = {
    rules: [
        {
            test: /\.(js|jsx)$/,
            exclude: /(node_modules)/,
            use: ['babel-loader']
        },
        {
            test: /\global.scss$/,
            use: [
                MiniCssExtractPlugin.loader,
                'css-loader',
                'sass-loader'
            ],
        },
        {
            test: /\.s[ac]ss$/i,
            exclude: [/\global.scss$/],
            use: [
                'style-loader', // creates style nodes from JS strings
                'css-loader', // translates CSS into CommonJS
                'sass-loader' // compiles Sass to CSS, using Node Sass by default
            ]
        },
        {
            test: /.(ttf|otf|eot|woff|woff2?|png|jpe?g|gif|svg|ico)$/,
            use: {
                loader: 'url-loader',
            },
        },
    ]
}

module.exports = (env, argv) => {
    if (argv.mode === 'development') {
        return {
            mode: 'development',
            entry: entryPoint,
            watch: true,
            output: {
                path: pathResolve
            },
            module: moduleRules,
            plugins: [
                new MiniCssExtractPlugin({
                    filename: '[name].css',
                    chunkFilename: '[name].css'
                }),
                new BrowserSyncPlugin({
                    host: envConf._host,
                    proxy: envConf._proxy,
                    files:
                        [
                            './*.php',
                            './page-templates/*.php',
                            './shared/*.php'
                        ]
                })
            ]
        }
    } else if (argv.mode === 'production') {
        return {
            mode: 'production',
            entry: entryPoint,
            output: {
                path: pathResolve
            },
            module: moduleRules,
            plugins: [
                new MiniCssExtractPlugin({
                    filename: '[name].css',
                    chunkFilename: '[name].[hash].css'
                })
            ]
        }
    }
}

It often happens to see this file splitted, one for the dev mode (ex. webpack.dev.js) and one for the production mode (ex. webpack.prod.js). Instead, I preferred to use a single file and pass the flags --mode=development and --mode=production in the respective scripts:

{
  "name": "wpwebpack",
  "version": "1.0.0",
  "description": "wp__webPack",
  "scripts": {
    "dev": "webpack --mode=development",
    "prod": "webpack --mode=production"
  },
  "dependencies": {
    "bootstrap": "^5.0.0-beta3",
    "bootstrap-icons": "^1.4.1"
  },
  "devDependencies": {
    "@babel/core": "^7.13.15",
    "@babel/plugin-proposal-class-properties": "^7.13.0",
    "@babel/plugin-syntax-dynamic-import": "^7.8.3",
    "@babel/preset-env": "^7.13.15",
    "@babel/preset-react": "^7.13.13",
    "babel-core": "^6.26.3",
    "babel-loader": "^8.2.2",
    "babel-preset-env": "^1.7.0",
    "browser-sync": "^2.26.14",
    "browser-sync-webpack-plugin": "^2.3.0",
    "classnames": "^2.3.1",
    "css-loader": "^5.2.1",
    "html-webpack-plugin": "^5.3.1",
    "mini-css-extract-plugin": "^1.4.1",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "sass": "^1.32.8",
    "sass-loader": "^11.0.1",
    "style-loader": "^2.0.0",
    "url-loader": "^4.1.1",
    "webpack": "^5.31.2",
    "webpack-cli": "^4.6.0"
  },
  "author": "Giacomo Corallini - Software Developer",
  "license": "ISC"
}

React Hello World! โšก

First of all, let's create a my-component/ folder in /src/app/ which will be the container for our files needed by the React application. Next creating my-component.js file. So, using the ReactDOM.render() method, React will render an element in the DOM, the file will look something like this:

import React from 'react';
import ReactDOM from 'react-dom';

import './my-component.scss';
import App from './App.jsx';

ReactDOM.render(<App />, document.getElementById('root'));

At this point we are ready to create our first component in React, we will call it App.jsx and pass it in input such as element in the render() method.

import React from 'react';

export default function App() {
    return (
        <div>
            <h1>Hello World! React app here !</h1>
        </div>
    );
}

WordPress template files

In the folder dedicated to WordPress template files creating a my-component-template.php file, in this case I used a folder to contain all the custom files of the theme: page-templates/.

<?php /* Template Name: My Component Template */ ?>

<?php get_header(); ?>

<h1>My Component Template</h1>

<div id="root"></div>

<?php get_footer(); ?>

<script src="<?php echo get_template_directory_uri() . '/public/my_component.js' ?>"></script>

This contain the target id of the React application container and the script tag that points directly to the js file generated by webpack in the /public folder. The get_template_directory_uri() method return the root path of the current theme.

Webpack firing

We just have to start Webpack, open the terminal, move to the root of the theme and then use the script $ npm run dev to run development mode. If everything went well (rarely ๐Ÿ˜…) browser-sync-webpack-plugin will open the browser in localhost:3000.

References

๐Ÿ™‹โ€โ™‚๏ธ ๐Ÿป

ย