{"id":56271,"date":"2022-12-27T13:01:36","date_gmt":"2022-12-27T07:31:36","guid":{"rendered":"https:\/\/www.tothenew.com\/blog\/?p=56271"},"modified":"2022-12-30T13:04:12","modified_gmt":"2022-12-30T07:34:12","slug":"loosely-coupled-integrate-react-js-into-drupal-sites","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/loosely-coupled-integrate-react-js-into-drupal-sites\/","title":{"rendered":"Loosely Coupled: Integrate React JS into Drupal sites"},"content":{"rendered":"<p>React is a JavaScript library that makes it painless to create interactive UIs. Unfortunately, embedding React into a Drupal site is not straightforward. In this article, I will show you relatively simple methods of how to do so.<\/p>\n<p>React can be integrated with Drupal in 2 ways:<\/p>\n<ol>\n<li>Fully decoupled<\/li>\n<li>Partially Decoupled (Loosely Coupled)<\/li>\n<\/ol>\n<h2>Fully Decoupled<\/h2>\n<p>In a fully decoupled CMS, the <strong>front-end and back-end are separated<\/strong>. We can also call this \u201cHeadless Drupal\u201d.<\/p>\n<p>The Drupal CMS handles the back-end. Initially, we need to set up our Drupal, build the content architecture and then expose this data as a web service API.<\/p>\n<p>Once we are ready with the JSON API exposing the required data, we now need to set up our Front-end (React) to consume those APIs and display data in the application. This can be done by using create-react-app or by react webpack setup and axios for APIs.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-56269\" src=\"\/blog\/wp-ttn-blog\/uploads\/2022\/12\/fully-decoupled-300x242.png\" alt=\"\" width=\"300\" height=\"242\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2022\/12\/fully-decoupled-300x242.png 300w, \/blog\/wp-ttn-blog\/uploads\/2022\/12\/fully-decoupled.png 396w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/p>\n<h2>Partially Decoupled (Loosely Coupled)<\/h2>\n<p>This approach will render your React app within a Drupal block. You could also put the React component into the theme. This method will allow you to include 3rd-party packages from npm, scale your application to many files and components, enable live editing in development mode, and help optimize things for speed and compatibility.<\/p>\n<h3>DON\u2019T USE CREATE-REACT-APP<\/h3>\n<p>Create-react-app is a nice tool that generates scaffolding for a React application. It sets up a modern web app by running one command. But when we need to build react blocks to embed into drupal, create-react-app is not so great. For instance, We do not need a public folder that create-react-app provides in this case, as all of this is handled by drupal twig templating. Also, we do not need all the pre-installed dependencies.<\/p>\n<h3>WHAT TO DO<\/h3>\n<h4>Goal<\/h4>\n<p>Connect a &#8220;Hello World!&#8221; React application to a Drupal theme or module.<\/p>\n<h4>Step 1: Create a custom module<\/h4>\n<p>In your custom module folder (\/modules\/custom) create a new folder for the custom module. Let&#8217;s call it <strong>react_module<\/strong>.<\/p>\n<p>In the react_module folder, add an <strong>react_module.info.yml<\/strong> file to register your module.<\/p>\n<pre>name: React Module\r\ndescription: Module for React code\r\npackage: Custom\r\ncore: 8.x\r\ncore_version_requirement: ^8 || ^9\r\ntype: module<\/pre>\n<h4>Step 2: Enable the new module<\/h4>\n<p>Enable the module by navigating to Extend (\/admin\/modules) in the Manage administration menu.<\/p>\n<h4>Step 3: Setup React App<\/h4>\n<ul>\n<li>Initialize a new react project inside <strong>\/modules\/custom\/react_module\/js\/react-app<\/strong>.<\/li>\n<\/ul>\n<pre>npm init<\/pre>\n<p>This will generate a basic package.json. We will place all the react code inside this folder.<\/p>\n<ul>\n<li>Next, we want to install latest stable version of node:<\/li>\n<\/ul>\n<pre>nvm install node\r\nnode -v &gt; .nvmrc<\/pre>\n<ul>\n<li>Now we have to install all the required packages.<\/li>\n<\/ul>\n<p>Dev dependencies:<\/p>\n<pre>npm install --save-dev @babel\/core @babel\/cli @babel\/preset-env @babel\/preset-react babel-loader webpack webpack-cli<\/pre>\n<p>Production dependencies:<\/p>\n<pre>npm install react react-dom<\/pre>\n<ul>\n<li>Add a webpack config<\/li>\n<\/ul>\n<p>This goes in a file called <strong>webpack.config.js<\/strong>.<\/p>\n<pre>const path = require('path');\r\n\r\nconst config = {\r\n  entry: '.\/src\/index.js',\r\n  devtool: (process.env.NODE_ENV === 'production') ? false : 'inline-source-map',\r\n  mode: (process.env.NODE_ENV === 'production') ? 'production' : 'development',\r\n  output: {\r\n    path: path.resolve(__dirname, 'dist'),\r\n    filename: 'app.bundle.js'\r\n  },\r\n  module: {\r\n    rules: [\r\n      {\r\n        test: \/\\.js$\/,\r\n        exclude: \/(node_modules)\/,\r\n        use: {\r\n          loader: 'babel-loader'\r\n        }\r\n      }\r\n    ]\r\n  },\r\n};\r\n\r\nmodule.exports = config;<\/pre>\n<p>We are configuring a process here that&#8217;ll take our source index.js file, and pass them through different build steps that will ultimately output a single, optimized, .js file.<\/p>\n<ul>\n<li>Configure package.json<\/li>\n<\/ul>\n<p>This tells webpack to either do a development (watch) or a production (build) <strong>build<\/strong>.<\/p>\n<pre>\"scripts\": {\r\n  \"build\": \"NODE_ENV=production webpack\",\r\n  \"watch\": \"webpack --watch --progress\"\r\n}<\/pre>\n<p>Configure how <strong>Babel<\/strong> works in package.json.<\/p>\n<pre>\"babel\": {\r\n  \"presets\": [\r\n    [\r\n      \"@babel\/preset-env\",\r\n      {\r\n        \"targets\": {\r\n          \"browsers\": [\r\n            \"IE &gt;= 11\",\r\n            \"last 3 versions\"\r\n          ]\r\n        }\r\n      }\r\n    ],\r\n    \"@babel\/preset-react\"\r\n  ]\r\n}<\/pre>\n<p>After following these steps you&#8217;re all set to have Babel transpile your JavaScript so you can use JSX and ES6+ features in your code. Webpack will bundle your custom code along with the required React, and ReactDOM libraries, as well as any other libraries you include using npm, into a single JavaScript bundle.<\/p>\n<h4>Step 4: Create a basic React App<\/h4>\n<p>Put this in <strong>src\/index.js<\/strong>. You can add other components within the render and scale your application to many files and components.<\/p>\n<pre>import React from 'react';\r\nimport ReactDOM from 'react-dom';\r\n\r\nconst root = ReactDOM.createRoot(document.getElementById('react-root'));\r\nroot.render(\r\n  &lt;h2&gt;Hello world!&lt;\/h2&gt;\r\n);<\/pre>\n<p><strong>root.render()<\/strong> (i.e. virtual dom) will look for an HTML element with the ID react-root, and replace it with the HTML markup produced by your React code. This is also known as binding the React application to the DOM.<\/p>\n<h4>Step 5: Include your React app in Drupal<\/h4>\n<ul>\n<li>Define a library for your React app.<\/li>\n<\/ul>\n<p>In your module, add a <strong>react_module.libraries.yml<\/strong> file as follows:<\/p>\n<pre>react-lib:\r\n  version: 1.x\r\n  js:\r\n    js\/react-app\/dist\/app.bundle.js: { minified: true }<\/pre>\n<ul>\n<li>Include the React app\u2019s Drupal library and target markup on a page. The React app will put itself within the react-root div since we have used <strong>document.getElementById(&#8216;react-root&#8217;)<\/strong> in Step 4.<\/li>\n<\/ul>\n<p>There are quite a few ways to do this.<\/p>\n<p>i) Using twig template (page, block, region etc):<\/p>\n<pre>{{ attach_library('react_module\/react-lib') }}\r\n&lt;div id=\"react-root\"&gt;&lt;\/div&gt;<\/pre>\n<p>ii) You can also use a render array to embed the app. For Instance, if you want to do things from a <strong>form alter<\/strong>:<\/p>\n<pre>$form['my_react_app'] = [\r\n  '#markup' =&gt; '&lt;div id=\"react-root\"&gt;&lt;\/div&gt;',\r\n  '#attached' =&gt; [\r\n    'library' =&gt; [\r\n      'react_module\/react-lib'\r\n    ],\r\n  ],\r\n];<\/pre>\n<p>iii) Define a block plugin<\/p>\n<p>This will output the DOM element to bind to, and attach the React application asset library to that block. Then whenever the block appears on the page the React application will load.<\/p>\n<p>Place this in <strong>modules\/react_module\/src\/Plugin\/Block\/ReactBlock.php<\/strong>:<\/p>\n<pre>&lt;?php\r\nnamespace Drupal\\react_module\\Plugin\\Block;\r\n\r\nuse Drupal\\Core\\Block\\BlockBase;\r\n\r\n\/**\r\n * Provides a 'ReactBlock' block.\r\n *\r\n * @Block(\r\n * id = \"react_block\",\r\n * admin_label = @Translation(\"React block\"),\r\n * )\r\n *\/\r\nclass ReactBlock extends BlockBase {\r\n\r\n  \/**\r\n   * {@inheritdoc}\r\n   *\/\r\n  public function build() {\r\n    $build = [];\r\n    $build['react_block'] = [\r\n      '#markup' =&gt; '&lt;div id=\"react-root\"&gt;&lt;\/div&gt;',\r\n      '#attached' =&gt; [\r\n        'library' =&gt; 'react_module\/react-lib'\r\n      ],\r\n    ];\r\n    return $build;\r\n  }\r\n}<\/pre>\n<p><strong>Clear drupal cache<\/strong> and the block will be available to place in any region. Go to <strong>\/admin\/structure\/block<\/strong> and click on \u201cplace block\u201d in the sidebar region and search for \u201cReact Block\u201d. Place the block and it will be available on the page.<\/p>\n<h4>Step 6: Run<\/h4>\n<p>Let\u2019s run a development build from within <strong>\/modules\/custom\/react_module\/js\/react-app<\/strong>:<\/p>\n<pre>npm run watch<\/pre>\n<p>Running this command will run the <strong>watch build scripts<\/strong> in package.json (Step 3). It will listen for changes to any of the files in <strong>src<\/strong> and automatically rebuild the assets in <strong>dist\/app.bundle.js<\/strong>.<\/p>\n<p>Open \/ Reload the page containing the target div from twig or form (Step 5). You should see the \u201cHello world!\u201d text in the target div.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-medium wp-image-56270\" src=\"\/blog\/wp-ttn-blog\/uploads\/2022\/12\/partially-decoupled-300x129.png\" alt=\"\" width=\"300\" height=\"129\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2022\/12\/partially-decoupled-300x129.png 300w, \/blog\/wp-ttn-blog\/uploads\/2022\/12\/partially-decoupled-1024x442.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2022\/12\/partially-decoupled-768x331.png 768w, \/blog\/wp-ttn-blog\/uploads\/2022\/12\/partially-decoupled-624x269.png 624w, \/blog\/wp-ttn-blog\/uploads\/2022\/12\/partially-decoupled.png 1301w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/p>\n<p>To create the production build, run:<\/p>\n<pre>npm run build<\/pre>\n<p>Do this whenever you&#8217;re ready to deploy your changes, and then commit the updated files to Git.<\/p>\n<h4>Final Folder structure:<\/h4>\n<pre>+-- react_module\r\n| +-- react_app.info.yml\r\n| +-- react_app.libraries.yml\r\n| +-- react_app.module (Defines Drupal hooks like hook_page_attachments or hook_form_alter)\r\n| +-- js\r\n| | +-- custom.js (Defines Drupal Behaviors)\r\n| | +-- react-app\r\n| | | +-- dist\/\r\n| | | | +-- app.bundle.js\r\n| | | +-- node_modules\/\r\n| | | +-- src\r\n| | | | +-- index.js\r\n| | | +-- package.json\r\n| | | +-- package-lock.json\r\n| | | +-- webpack.config.js<\/pre>\n<p><strong>Note<\/strong>: If any of the changes are not reflected on page refresh, try <strong>clearing browser or Drupal cache<\/strong>.<\/p>\n<h4>Further for your understanding<\/h4>\n<ul>\n<li>Try to add a React component to the page via a custom theme.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<div class=\"ap-custom-wrapper\"><\/div><!--ap-custom-wrapper-->","protected":false},"excerpt":{"rendered":"<p>React is a JavaScript library that makes it painless to create interactive UIs. Unfortunately, embedding React into a Drupal site is not straightforward. In this article, I will show you relatively simple methods of how to do so. React can be integrated with Drupal in 2 ways: Fully decoupled Partially Decoupled (Loosely Coupled) Fully Decoupled [&hellip;]<\/p>\n","protected":false},"author":1124,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":396},"categories":[3602,3038],"tags":[4862,5067,5068,4064,5069],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/56271"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/1124"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=56271"}],"version-history":[{"count":1,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/56271\/revisions"}],"predecessor-version":[{"id":56272,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/56271\/revisions\/56272"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=56271"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=56271"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=56271"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}