Skip to main content

How to access Drupal data in a React widget on a Drupal 8 site

Front-end Development
Drupal

In my previous article, I demonstrated a quick and easy way to get a ReactJS widget embedded on a Drupal site. This got you up and running quickly, but it didn't provide ways to get Drupal data into the React widget. In this article, I will go over two ways to read Drupal data from within React. Like last time, the complete code changes are available on Github.

Method #1: Data Attributes

The first method of passing data from Drupal to React will be to add data attributes to the element on which the React widget is mounted. We could pass individual pieces of data as individual data attributes. But in this example, we'll encode them into JSON and pass them through a single data attribute.

For our example data, we'll simply pass the PHP time() function.

While we're at it, let's give the element a better ID. The default element ID for CRA is root which is terribly uninformative. Since our block name is React Example, let's change the element ID to react-example.

In the Drupal block file (src/Plugin/Block/ReactExampleBlock.php), we update the render element:

    $build[] = [
      '#type' => 'container',
      '#attributes' => [
-       'id' => 'root',
+       'id' => 'react-example',
+       'data-drupal' => json_encode([
+         'time' => time(),
+       ]),
      ],

And in the index.js file of our React project, we store the element as a variable since we'll use it twice:

+ const mount = document.getElementById('react-example');

ReactDOM.render(
  <React.StrictMode>
-   <App />
+   <App data={JSON.parse(mount.dataset.drupal)} />
  </React.StrictMode>,
- document.getElementById('root')
+ mount
);

Finally, we'll output the data in App.js:

-function App() {
+function App({ data }) {
   return (
     <div className="App">
       <header className="App-header">
         <img src={logo} className="App-logo" alt="logo" />
         <p>
-          Edit <code>src/App.js</code> and save to reload.
+          {JSON.stringify(data)}
         </p>

If you're successful, you should see encoded JSON on the data-drupal attribute:

<div id="react-example" data-drupal="{&quot;time&quot;:1608581030}">

And a decoded version within your React app.

Method #2: Drupal Settings Global Variable

The first method is probably fine if you don't have a lot of data. Otherwise, a better way to pass through data is with the Drupal Settings JavaScript variable. One benefit of using Drupal Settings is that you don't have to serialize/unserialize your data. This is done automatically for you.

We add values to the Drupal Settings JavaScript variable by attaching them similar to the JS library.

First, we need to add this dependency to the library definitions for our JS files by modifying react_example.libraries.yml:

react_example_dev:
  version: 1.x
  js:
    "http://localhost:3000/static/js/main.js":
      { type: external, minified: true, async: true }
+ dependencies:
+    - core/drupalSettings

react_example_prod:
  version: 1.x
  js:
    js/react_example/build/static/js/main.js: { preprocess: false, async: true }
+ dependencies:
+     - core/drupalSettings

Then in src/Plugin/Block/ReactExampleBlock.php, add this:

      '#attached' => [
        'library' => [
          empty($_ENV['PLATFORM_PROJECT'])
            ? 'react_example/react_example_dev'
            : 'react_example/react_example_prod',
        ],
+       'drupalSettings' => [
+         'react_example' => [
+           'time' => time(),
+         ],
+       ],
      ],

And then we can read it from the global variable in index.js:

  <React.StrictMode>
-   <App />
+   <App data={drupalSettings.react_example} />
  </React.StrictMode>,