11/30/2021

Drupal 8 React

Creating a React Application with Headless Drupal 8

Submitted by Troy Kearney on Tue, 03/28/2017 - 15:43

Overview:

And this is how the React block renders itself on our Drupal 8 website and makes the website a progressively decoupled app. For one of our clients, we implemented this solution to create a budget planner and currency calculator for a giant travel retail outlet and helped boost the sales just after 3 months of its deployment. While your React application is on, you have to create some end-points in Drupal that will be data sources for the React app. Drupal 8 has a built-in full REST API, which makes this CMS a winner among other CMS’s. The documentation on this API is on the Drupal REST API page. The REST UI module allows you to make some new and clean adjustable.

  • Setting up Drupal to enable RESTful Services

  • Implementing React with Drupal

    • Get a list of articles

    • Add a new article

For this tutorial I will go over how to setup a Drupal 8 site with RESTful services as well as setting up a simple React application which will retrieve a list of Articles and allow for the creation of articles for logged in users with the proper credentials.

Before working with React we will need to configure Drupal to provide us with some RESTful services.

The first step you will need to take is downloading and enabling the following modules: RESTful Web Services (rest), Serialization (serialization), and REST UI (restui) (https://www.drupal.org/project/restui). The Serialization module is required for RESTful Web Services and the REST UI only provides a nice interface from which you can configure what pieces of content will be available via REST requests and which formats they are available.

Next head over to /admin/config/services/rest and select the actions you want to have available. In this tutorial I only want the Content or node/{node} resource enabled by both GET and POST requests. On the next page set the ‘Accepted request formats’ to JSON and the ‘Authentication providers’ to cookie. The ‘Authentication providers’ selection is required and at the moment we only have cookie available.

To be able to get all nodes on the front page, make a new display for the Frontpage view. The type of display should be REST export. In the Serialization settings set the Accepted request format to json. Give this new display a path of /node/rest

Let’s test out our GET service using curl before we move any further.

This should return a JSON result of the node with the nid 1.
Before making a POST request, we will need to create a JSON file with some data to create a new node and obtain a X-CSRF-Token. To obtain the Drupal provided cookie, open up Chrome Dev Tools, select the Network tab, pick one of the loaded resources, select the Headers tab, and copy the cookie value under the Request Headers. Make a file called node.json and add the following JSON to the file.

For the X-CSRF-Token simply navigate to /rest/session/token and copy and save the string on that page. Now create your POST request as such:

Note that the path /entity/node is used for 8.2.x. For 8.3.x /node can be used.

Now let’s get to the fun stuff!
On your server navigate to sites/<site name>/files and create a file called headless.html. Here is some boilerplate code to get us started.

Navigate to http://localhost/sites/default/files/headless.html and you should be greeted with a “Hello Headless Drupal!” message.
Now include React, React-DOM, and Babel from a CDN in the head.

And inside of a script tag with type=”text/babel”, we can add some basic React to make sure everything is in working order.

Now you should see the message “Hello Drupal from React!”

To start making calls to Drupal’s RESTful services we will need to include a HTTP client. I am using axios in this tutorial. Include the below script in the head of your HTML.

Drupal 8 React Module

Above the Hello Drupal from React message, add the following lines to perform an axios get request for the X-CSRF-Token. This code will confirm that axios is working properly and can be commented out until it is need later.

Drupal 8 React

With all this working we can get started with building the React components.

We begin with an App component which will be the starting point for the entire application.

This will initialize the state with an empty array of nodes. We then populate the array in the componentWillMount() lifecycle method by making a get request for the nodes provided by the view at /node/rest created earlier.

In the render function we’re going to call the NodeList component which simply maps out each of the Nodes provided from App’s state.

A simple NodeList component to map out each of the Nodes.

The Node component is used as an instance of a node, which is being used above.

At the bottom of the file remember to render out the App component.

Now you should be able to visit http://localhost/sites/default/files/headless.html and you will see a list of node content. All that’s left now is to create a form component which will allow us to create a new Article node.

There you have it! A simple approach to decoupling Drupal with React.

The full code for this tutorial is available here

When the conversation of decoupled comes up, a lot of times it revolves around chopping the entire front end off the site and replacing it with something like React or Vue. This is not only a major task for a development team but it could also be a hard sale to clients, since it is going to impact future improvements and possibly take longer to initially develop.

But that doesn’t mean we can’t start adding our favorite JS framework into existing sites and showing off the cool interactions React/Vue/Angular give us.

The Fast Way

When prototyping something internally or for a client, I usually use the following setup. It has a number of drawbacks, but it is fast. Using Create React App and Drupal’s Block Class plugin, I just make a block with the div I’m targeting. In my Block class I just change the markup to something like:

And then in my Create React App, edit the index.js file with the following:

From here I can build out my React app and it will load in that div. The biggest issue I have with this is the page source will show an empty div on load and then populate after the JS has time to run and make any queries to whatever API endpoint I’m using, either external or from my Drupal site.

This approach combined with a CSV migration of some data into your Drupal site and a charting program like Google Charts (the React version) can create some impressive visuals quickly.

React Hydration

In the newest versions of React, ReactDOM got a new toy, hydrate. Now you can either call render() or hydrate() when building a new app. When I saw this new method, I was super excited. In the past to try and get server rendered code outside of a Node.js environment, I tried integration PHPV8 and all kinds of other hacks to server render my initial load of the React app.

Render vs Hydration

So what is the difference in these two methods. Render will effectively replace what is in the targeted DOM element with the new React app, attaching all the event listeners to the new rendered app.

Hydration on the other hand, tells React to look at the markup as it’s already been rendered and attach the event listeners to the code. This is pretty easy in a Node environment as I can run the same React code on the server and in the client to make sure it always matches up. But I have to be extra careful of my Drupal markup to make sure it matches exactly what my React app is going to render.

A simple counter example

Github Repo: https://github.com/dgading/d8_react_hydrate_example

To try and test this, I built a very simple block example. I hope to expand into something bigger using something like Views, but that is for a future project.

The Module

The module itself is very simple, an info file, a libraries file, a JavaScript file, and a Block plugin file.

The Info File

The info file is the only file needed to enable a module on your Drupal site. I’m not doing anything custom in this file or requiring any additional modules.

The Libraries File

Drupal 8 form

The libraries file in the module is where we can add our custom JS and the required React files. I used this React doc as my inspiration on which files to load.

To breakdown the file above, I am loading two separate libraries. The “react” library has all the files from the React docs and my “countdown” library adds my custom JS code and also requires drupalSettings from Drupal core. I’m using drupalSettings to help me pass variables from my PHP code to my React as initial props. More on that in a bit.

I separated this into two libraries so I could create more examples and include React and not have to also include my countdown code.

The Block

Before I go through the React code, I want to quickly explain the Block I am creating. Much like the quick example above, I need to create a DOM element that I can load React into. The extra piece here is I want to also add some data from server to the element and pass that same data to my React app. This should probably be done with a Twig template and some variables, but I am still just prototyping, so I hardcoded the markup.

In the build function for this block there are two things happening, we are setting a variable to the current node id and we are returning our markup and attaching our JS to the block. Finally we are passing the node id to drupalSettings so we have it available for our React app.

Drupal 8 React Native

The React App

Finally the React app. This is what we’ve been waiting for. Instead of building an entire Webpack build system, I am using the instructions from the React docs for adding React to an HTML file.

In the code above, we are rendering a React component that takes one prop, reactNID, and load it into state. Once it is in our state, we render it in our h1 element and after the component mounts we decrement the number by one each second. When it reaches 0, we unmount the component.

This is our code to build the app. The hydrate method is loading our component, setting our intial prop by looking at the drupalSetting variable we passed to window in our Block code, and targeting our DOM element to load the app into. Since window.drupalSettings.reactNID and our markup from the Block build function are identical, React will start the countdown like it built the the app itself.

Conclusion

While this isn’t a very complex example, I feel it is a good place to start exploring how to add a modern interaction layer to your Drupal site without completely decoupling.

Drupal 8 React Theme

I am also writing up a similar pattern for WordPress which I hope to have finished soon.