Getting Started with React in Drupal 8

If you’ve done any web development in the last couple of years, then I’m sure you noticed that JavaScript libraries and frameworks have become popular. The three that come to mind are React, Vue.js, Angular but there are many, many more.

These libraries and frameworks are great for building complex JavaScript applications. Also, being written in JavaScript means they can easily be embedded into a Drupal site.

In this tutorial, I’ll show you how to embed and compile a React application using two methods: Lavavel Mix and Webpack. If you want to use React then you’ll realize very quickly that you can’t just write JavaScript and have it work out-of-the-box, it needs to be compiled. That’s why I’ll show you two ways you can compile React locally without using a CDN.

This tutorial is targeted towards developers who know how to create a Drupal module, use the npm command and know basic React.

A copy of all the code can be found at 💻 https://github.com/WebWash/ww_react_examples.

Compile React using Laravel Mix

In this section, we’ll create a Drupal module which will implement a custom block that will embed a basic React application.

So let’s start things off by creating a module called ww_react_basic.

Create ww_react_basic Module

1. Go into your modules directory and create a folder called ww_react_basic. In the directory, create a file called ww_react_basic.info.ymland add the following:

name: 'React App Basic'
type: module
description: 'Basic set up of a react app.'
core: 8.x
package: 'WebWash Examples'

2. In the same directory create a ww_react_basic.libraries.yml and create the following asset by adding:

react-basic:
  js:
    js/dist/index.js: { preprocess: false }

3. We’ll use a block to render the React application so let’s go ahead and create a block plugin. Create a file in the following path src/Plugin/Block/ReactBasicBlock.php. In the PHP file add the following code:

<?php

namespace Drupal\ww_react_basic\Plugin\Block;

use Drupal\Core\Block\BlockBase;

/**
 * Provides a 'ReactBasicBlock' block.
 *
 * @Block(
 *  id = "ww_react_basic_block",
 *  admin_label = @Translation("React basic block"),
 * )
 */
class ReactBasicBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
    $build = [];
    $build['react_basic_block'] = [
      '#markup' => '<div id="basic-app"></div>',
      '#attached' => [
        'library' => 'ww_react_basic/react-basic'
      ],
    ];
    return $build;
  }
}

This block is very basic all we’re doing is returning a render array with the attached library, 'ww_react_basic/react-basic'

So far all we’ve done is, create a module, declare a library and create a block plugin.

Now let’s create the actual React application.

Create React Application

We’ll store the React application in the module for simplicity. However, an application can be stored in the theme as well.

Download Packages

1. Go ahead and create a js folder, and in this folder run npm init. After you’ve completed the prompts you should have a package.json file with the following path:

js/package.json

2. Then download React using NPM:

npm install react react-dom -s

3. Then download Laravel mix:

npm install laravel-mix cross-env --save-dev

Configure Laravel Mix

So we’ve downloaded all the required packages, let’s configure Laravel Mix. This will be used to compile React.

1. Create webpack.mix.js in the js directory. Then add the following in the file:

const mix = require('laravel-mix');

mix.react('src/index.js', 'dist');

2. Now open up the package.json file and add the following to the scripts section:

"scripts": {
  "dev": "npm run development",
  "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
  "watch": "npm run development -- --watch",
  "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
  "prod": "npm run production",
  "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},

This adds NPM scripts which you’ll need to use to compile React.

The two important scripts are:

Use this script to automatically compile when a file is changed. This should be used locally while developing a site.

$ npm run watch

This script should be run when you’re ready to deploy to production. It’ll minify JavaScript file to reduce the size.

$ npm run production

At this point we’ve created the following files:

  • js/package.js
  • js/webpack.mix.js

Create React Files

Now let’s write some React code. As mentioned above this React application is very basic. All it’ll do is load a simple component.

1. In the js directory within the module, create a folder src.

2. Create index.js and add the following code:

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

import Page from './components/Page';

ReactDOM.render(
<Page />,
  document.getElementById('basic-app')
);

All we’re doing here is importing the Page component and using ReactDOM.render to attach/render the React to <div id="basic-app"></div>, which is added by the block that we implemented.

Make sure you use the same ID for document.getElementById('basic-app') and <div id="basic-app"></div>. If you don’t then the application won’t load.

3. Create another JavaScript file at src/components/Page.js and add the following code:

import React from "react";

const Page = () => (
  <div>
    <p><strong>React</strong> loaded using Laravel Mix</p>
  </div>
);

export default Page;

Once complete the following files should’ve been created:

  • js/src/index.php
  • js/src/components/Page.js

Test Application

Now that everything has been configured let’s test it all out.

1. Log into your Drupal site and install the “React App Basic” module which we just created.

2. Then Structure, “Block layout” and “Place block” in the “Sidebar first” region.

Search for “react basic block”, which should appear, then click on “Place block”.

3. Leave the “Configure block” options as they are, then click on “Save block”.

4. Jump into your Terminal and cd in the js and run npm run dev. This will compile the React application.

If all you’re seeing is an empty block, open the inspector and look for errors. If you haven’t compiled your JavaScript you’ll see a 404 error.

Also, if you have JavaScript aggregation turned on make sure you rebuild the site cache. Go to Configuration, Performance or run drush cr.

Once everything is compiled you should see the following:

Compile React using Webpack

In the last section we looked at how to compile React using Laravel Mix and we created a very basic application.

Now let’s take things one step further and create an application which will use JSON:API to pull in data from Drupal, and we’ll use Webpack to compile everything.

Create ww_react_list Module

Let’s start things off by creating another module and this time we’ll call it; ww_react_list.

1. Create a folder called ww_react_list and within it create a file ww_react_list.info.yml, add the following into it:

name: 'React App List'
type: module
description: 'Basic set up of a react app.'
core: 8.x
package: 'WebWash Examples'

2. In the module directory, create a file ww_react_list.libraries.yml and add the following to it:

react-list:
  js:
    js/dist/index.js: { preprocess: false }

3. Create a block plugin at the following path: src/Plugin/Block/ReactListBlock.php with the following in it:

<?php

namespace Drupal\ww_react_list\Plugin\Block;

use Drupal\Core\Block\BlockBase;

/**
 * Provides a 'ReactBasicBlock' block.
 *
 * @Block(
 *  id = "ww_react_list_block",
 *  admin_label = @Translation("React list block"),
 * )
 */
class ReactListBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
    $build = [];
    $build['react_list_block'] = [
      '#markup' => '<div id="list-app"></div>',
      '#attached' => [
        'library' => 'ww_react_list/react-list'
      ],
    ];

    return $build;
  }
}

All we have done so far is create a module with an info.yml and libraries.yml file and we implemented a custom block.

Below is a list of the files we’ve created:

  • ww_react_list.info.yml
  • ww_react_list.libraries.yml
  • src/Plugin/Block/ReactListBlock.php

Create React Application

Create a js folder in the module you created in the previous section. Then open up your terminal and run npm init.

Download Packages

Once you have a package.json, install the following packages:

npm install react react-dom -s

And

npm install --save-dev @babel/core @babel/preset-env babel-loader @babel/preset-react webpack webpack-cli

Configure Webpack and Babel

1. In the same directory, create a .babelrc file and add the following:

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ]
}

2. Create a webpack.config.js with the following:

module.exports = {
  entry: './src/index.js',
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: ['babel-loader']
      }
    ]
  },
  resolve: {
    extensions: ['*', '.js', '.jsx']
  },
  output: {
    path: __dirname + '/dist',
    filename: 'index.js'
  }
};

3. Open package.json and add the following scripts:

"scripts": {
  "dev": "webpack -d --config ./webpack.config.js",
  "watch": "webpack -d --watch --config ./webpack.config.js",
  "prod": "webpack -p --config ./webpack.config.js"
},

So far the files we created are:

  • js/package.json
  • js/.babelrc
  • js/webpack.config.js

Create React Files

Now that we have everything set up let’s write some React code.

1. Create index.js in src directory and add the following into it:

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

import App from './components/App';

ReactDOM.render(
<App />,
  document.getElementById('list-app')
);

Now go ahead and create the following components.

2. js/src/components/App.js:

import React from "react";
import ArticleBlock from  "./ArticleBlock";

/**
 * Loads app.
 *
 * @returns {*}
 * @constructor
 */
const App = () => (
  <div>
    <ArticleBlock />
  </div>
);

export default App;

This component is used to load the ArticleBlock component.

3. js/src/components/ArticleBlock.js:

import React from "react";
import Article from "./Article";

/**
 * Lists out articles.
 */
class ArticleBlock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [],
      articleData: null,
      loaded: false,
    };
    this.handleReturnClick = this.handleReturnClick.bind(this)
  }

  /**
   * Load articles via Drupal JSON:API.
   */
  componentDidMount() {
    fetch('/jsonapi/node/article?sort=-created&page[limit]=3')
      .then(response => response.json())
      .then(data => this.setState({ data: data.data, loaded: true }));
  }

  /**
   * Add article data to state when clicking on an article link.
   *
   * @param article
   * @param e
   */
  handleClick(article, e) {
    e.preventDefault();
    this.setState({
      articleData : article
    });
  }

  /**
   * Returns article data from state and displays list.
   * @param e
   */
  handleReturnClick(e) {
    e.preventDefault();
    this.setState({
      articleData : null
    });
  }

  render() {

    const { data, articleData, loaded } = this.state;

    if (!loaded) {
      return <p>Loading ...</p>;
    }
    if (data.length === 0) {
      return <p>No results</p>;
    }

    // Display actual article.
    if (articleData) {
      return <Article article={articleData} returnClick={this.handleReturnClick} />;
    }

    // Display list of articles.
    return (
      <div>
        <ul>
          {data.map(article =>
            <li key={article.id}>
              <a href="#" onClick={this.handleClick.bind(this, article)}>{article.attributes.title}</a>
            </li>
          )}
        </ul>
      </div>
    );
  }
}

export default ArticleBlock;

This component does a lot of heavy lifting. In the componentDidMount() method, we get the latest 3 articles from Drupal via JSON:API and save the results in the component state. This component also has two click events: handleClick() and handleReturnClick().

4. js/src/components/Article.js:

import React from "react";

/**
 * Displays an article's title, created and body field.
 *
 * @param article
 * @param returnClick
 * @returns {*}
 * @constructor
 */
const Article = ({ article, returnClick }) => {
  const { attributes } = article;
  return (
    <div>
      <h3>{attributes.title}</h3>
      <span>{attributes.created}</span>
      <div dangerouslySetInnerHTML={{__html: attributes.body.processed}} />
      <hr />
      <a href="#" onClick={returnClick}>&lt; Back to list</a>
    </div>
  );
};

export default Article;

This Article component is used to display individual articles from the JSON:API results.

Test Application

Seeing as we have everything set up and configured. Let’s test it all out.

1. First, go ahead and make sure you install the “React App List” module which we just created.

 

 

2. Install the JSON:API module. It ships with Drupal core so you won’t have to download the module.

3. Go to Structure, “Block layout” and add the “React list block” to the sidebar first region.

4. And don’t forget to create a few articles.

Now that we have Drupal configured and ready to go. The last thing we need to do is compile the JavaScript which we created.

Open up your terminal and run npm run dev with in the js directory.

If you get a missing script: dev error. Make sure you’ve added the scripts changes into package.json.

Once everything has been compiled you should see the following block in the sidebar first region:

Summary

As you can see setting up React in Drupal takes a bit of effort. It’s only worth using it if you’re trying to create a complex JavaScript application such as a calculator or you want to display some data using a 3rd party library.

But if all you want to do is write a bit of simple JavaScript to manipulate the DOM with jQuery then look at writing a Drupal behavior.

FAQs

Q: Compiled React using Laravel mix and nothing happens. No errors.

Try rebuilding the site cache.

Q: I don’t want to type all this code. Do you have a copy of the modules and react applications in Git.

Yes, https://github.com/WebWash/ww_react_examples

 

Ivan Zugec

About Ivan Zugec

Ivan is the founder of Web Wash and spends most of his time consulting and writing about Drupal. He's been working with Drupal for 10 years and has successfully completed several large Drupal projects in Australia.

3 thoughts on “Getting Started with React in Drupal 8”

Leave a Comment

You have to agree to the comment policy.

This site uses Akismet to reduce spam. Learn how your comment data is processed.