How to wire ReactJS app to a Drupal 8 controller

By Kevin, July 18th, 2019

On most of the projects I work on these days, I typically have requirements at a certain point where data from the application has to meet ReactJS in the frontend at the theme layer.

We have a very good system in place for working with the theme layer, the backend, and frontend components powered by ReactJS. One example of this is a site search. At its basic level, a site search is comprised of a single text input field (typically a fulltext search field), one or more facets (taxonomy, search modifiers), and a sidebar of promotional content or 'best bet' style result.

We want to achieve two things here:

  • Provide the search page to Drupal, without Views, accessible by all users
  • Allow the frontend developer to work independent, without having to know or understand Drupal and get into the theming layer

Most if not all frontend deliverables are done in Pattern Lab. Integrating that work occurs between either myself or delegated to someone on the team with oversight. This separation maximizes creativity on the frontend without slowing them down, and we fill in the backend gaps.

In this case, search data comes from an external Solr or similar search application. Our searches tend to be custom, so while the Search API crawls and indexes content, we get a bit more closer to the metal with the user facing implementation. This is why we are not using Views to do this. Instead, we use a mix of Guzzle HTTP calls or Solarium client (PHP Solr client) depending on what we are talking to. Wielding more power here gives you a lot more control; there are just some cases that Views will not solve for you.

We start by creating a custom module and provide it a routing file with a single route at "/search":

mymodule_search.search:
  path: '/search'
  defaults:
    _controller: '\Drupal\mymodule_search\Controller\SearchController::index'
    _title: 'Search'
  requirements:
    _permission: 'access content'

This maps to a controller that returns just the markup container that ReactJS is looking for. Over in our controller class, we create the method the route is looking for and return a response:

<?php

namespace Drupal\mymodule_search\Controller;

use Drupal\Core\Controller\ControllerBase;

/**
 * Class SearchController
 * @package Drupal\mymodule_search\Controller
 */
class SearchController extends ControllerBase {

  /**
   * @return array
   */
  public function index() {
    return ['#markup' => '<div class="js-search-mountnode" data-id="listing"></div>'];
  }
}

When the page loads, that HTML is returned by the controller method and the ReactJS events trigger. ReactJS populates the div container with data. In between, the ReactJS app is making requests against other backend APIs that we have that make requests on its behalf and return a response in the JSON format that the ReactJS app is expecting. Below, the ReactJS app loads and makes a request which our backend makes multiple requests against (multiple data sources) and returns the results back to be rendered:

Search in action

This is a rather simple pattern employed on a lot of our Drupal 8 projects. It works very well under the context of controllers, blocks, or Paragraphs and no contributed modules are needed to pull this off - just Javascript and Drupal.