Embedding a create-react-app project into a CakePHP 4 View

Written by James McDonald

August 21, 2020

Updated: 17 May 2022

Step 1 – Create the React App

I find it good to create the React project inside my CakePHP APP directory so I can keep it under the same git repository. To create the react project use create-react-app:

cd /var/www/wms/src
mkdir React
npx create-react-app edi-app

# My CakePHP project is in /var/www/wms and the react app is as follows
# /var/www/wms/src/React/edi-app

Remember to add node_modules to .gitignore, if it isn’t there already so you don’t bloat your repo


Step 2 – Customize scripts build

Next edit your edi-app/package.json file to create a custom build entry. The build entry does 3 things:

  1. Compiles a production build putting it in the default build directory in the react project,
  2. removes the currently deployed react directory from your CakePHP WWW_ROOT directory and
  3. copies over the latest production build react files to your CakePHP 4 webroot


"scripts": {
    "start": "react-scripts start",
     "build": "react-scripts build && rm -rf ../../../webroot/edi-app && cp -rv build ../../../webroot/edi-app",
    "test": "react-scripts test",
    "eject": "react-scripts eject"

Each time you are happy with your react project copy it across to the CakePHP webroot using npm run build

In the above example the files in /var/www/wms/src/React/edi-app/build are copied to /var/www/wms/webroot/edi-app

This is the contents of /var/www/wms/webroot/edi-app:


The asset-manifest.json file

When you do a production build of your react project it includes a asset-manifest.json

  "main.css": "/static/css/main.81a20791.chunk.css",
  "main.js": "/static/js/main.821121cc.chunk.js",
  "main.js.map": "/static/js/main.821121cc.chunk.js.map",
  "static/js/1.b3836c5f.chunk.js": "/static/js/1.b3836c5f.chunk.js",
  "static/js/1.b3836c5f.chunk.js.map": "/static/js/1.b3836c5f.chunk.js.map",
  "runtime~main.js": "/static/js/runtime~main.229c360f.js",
  "runtime~main.js.map": "/static/js/runtime~main.229c360f.js.map",
  "static/css/main.81a20791.chunk.css.map": "/static/css/main.81a20791.chunk.css.map",
  "index.html": "/index.html",
  "precache-manifest.00bfcb3f81f91832033cf3827e46d489.js": "/precache-manifest.00bfcb3f81f91832033cf3827e46d489.js",
  "service-worker.js": "/service-worker.js"

Newer versions of create-react-app have a different layout for the asset-manifest.json file:

  "files": {
    "main.css": "/static/css/main.ccfeb934.css",
    "main.js": "/static/js/main.3df63a03.js",
    "index.html": "/index.html",
    "main.ccfeb934.css.map": "/static/css/main.ccfeb934.css.map",
    "main.3df63a03.js.map": "/static/js/main.3df63a03.js.map"
  "entrypoints": [

Step 3 – Implement a ReactEmbed Component to Read asset-manifest.json

To allow the Controller Action to open and read the asset-manifest.json we can use a custom component:


// src/Controller/Component/ReactEmbedComponent.php

namespace App\Controller\Component;

use Cake\Collection\Collection;
use Cake\Controller\Component;
use Cake\Http\Exception\NotFoundException;

class ReactEmbedComponent extends Component
    protected $controller = null;

    public function initialize(array $config): void

     * @param string $subFolder Subfolder webroot/shipment-app <= shipment-app is subfolder
    public function getAssets($subFolder)
        $manifest = $this->assets($subFolder);

        return $this->parseManifest($manifest, $subFolder);

     * @param string $subFolder Folder that the react app is in
     * e.g. subFolder is webroot/pick-app
     * @return object
     * @throws NotFoundException
    public function assets($subFolder): array|null
        $assetManifest = WWW_ROOT . $subFolder . DS . 'asset-manifest.json';

        $file = @file_get_contents($assetManifest);

        if ($file === false) {
            throw new NotFoundException($assetManifest . ' missing');

        $manifest = json_decode($file, true);

        if (array_key_exists('entrypoints', $manifest)) {
            // just entry points
            $manifest = $manifest['entrypoints'];

        return $manifest;

    protected function parseManifest($manifest, string $subFolder): array
        $manifest = new Collection($manifest);

        $manifest = $manifest->map(function ($asset) use ($subFolder) {
            return DS . $subFolder . DS . $asset;
        })->groupBy(function ($asset) {
            $parts = explode('.', $asset);

            // get the .css or .js part for grouping
            $extension = end($parts);

            return $extension;

        // not sure if this can happen but just in case either expected key is missing
        $manifest = $manifest + [
            'js' => [],
            'css' => []

        return $manifest;

Step 4 – Edit Controller/ShipmentsController.php

In the Controller load the ReactEmbedComponent we will use it to read asset-manifest.json and return an assets array

// src/Controller/ShipmentsController.php

class ShipmentsController extends AppController
    public function initialize(): void

     * Edit method
     * @param  string|null                                        $id Shipment id.
     * @return \Cake\Http\Response|null|void                      Redirects on successful edit, renders view otherwise.
     * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
    public function edit($id = null)
        $assets = $this->ReactEmbed->getAssets(

        $baseUrl = $this->request->getAttribute('webroot');

        $productTypes = $this->Shipments->ProductTypes->find('list', ['conditions' => ['active' => 1]]);

        $this->set(compact('assets', 'baseUrl', 'productTypes'));

Example of assets array generated by ReactEmbedComponent

Step 5 – Modify templates/layout/default.php

Edit your CakePHP 4 layout. Normally when using the CSS helper all the CSS links go into the default ‘css’ block. Putting a fetch statement in for ‘react-css‘ block will allow you to embed the CSS after your general site CSS and make sure your React styles will be after and therefore over-ride and take precedence over the site CSS.

    <?= $this->Html->charset() ?>
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title><?= h($this->fetch('title')) ?></title>
    <?= $this->fetch('meta') ?>
    <?= $this->fetch('css') ?>
    <?= $this->Html->css('main'); ?>
    <?= $this->fetch('cust-css'); ?>
    <?= $this->fetch('react-css'); ?>
    <?= $this->Html->meta(['name' => "google", 'content' => "notranslate"]); ?>

Notice the /wms/edi-app/static/css/main.ccfeb934.css file is after the other site CSS

Do the same thing for the JavaScript assets. Modify the default layout to have a custom script block. In this example I called it ‘from_view’

echo $this->fetch('tb_body_start');
echo $this->fetch('tb_flash');
echo $this->fetch('content');
echo $this->fetch('tb_footer');
echo $this->fetch('script');
echo $this->fetch('postLink');
echo $this->fetch('from_view');
echo $this->fetch('tb_body_end');


Step 6 – Add a root node and react configuration to templates/Shipments/edit.php

Here we have the view template where we assign the react js and css assets to their respective blocks and provide some vital configuration for the react app to function.

  • Note the csrfToken which the react app needs to send back to CakePHP 4 for CakePHP 4 CSRF Middleware to trust what react sends to it.
  • a <div id="root" data-baseurl="/wms/"></div> is where the react app will load and the data-baseurl attribute will allow the react app to know where to post back to.

<?php echo $this->Html->scriptBlock(sprintf(
    'var csrfToken = %s;',
)); ?>

<?php foreach ($assets['css'] as $style) : ?>

    <?php $this->Html->css($style, [
        'block' => 'react-css',
    ]); ?>

<?php endforeach; ?>

<?php foreach ($assets['js'] as $script) : ?>

    <?= $this->Html->script($script, ['block' => 'from_view']); ?>

<?php endforeach; ?>

<?= $this->Html->tag('div', null, [
    'data-baseurl' => $baseUrl, 
    'id' => 'root',
]); ?>

Step 7 – Implement CORS Middleware

Implement CORS Middleware in CakePHP 4 to allow calls to CakePHP 4 from your React development environment URL which is usually http://localhost:3000 also handle the pre-flight options check with the appropriate headers.


Step 8 – Make the CakePHP 4 Backend API URL available to your React Dev Environment

In the CRA (Create React App) project specify the the CakePHP  URL in index.js

// src/React/edi-app/src/index.js

import React from "react";
import ReactDOM from "react-dom";
import Root from "./Components/Root";
import 'bootstrap/dist/css/bootstrap.min.css';

const root = document.getElementById("root");

if (process.env.NODE_ENV === 'development') {
    root.setAttribute('data-baseurl', '');

const baseUrl = root.getAttribute("data-baseurl");

ReactDOM.render(<Root baseUrl={baseUrl} />, root);

So now when you run npm run start the dev react app will have your react devel URL available


  1. Brad

    Hey James, this was super useful. Thanks for sharing.
    FYI – if you’re using React Router and have different/dynamic paths, to prevent Cake throwing Not Found errors you can add a wildcard to the routes and put it at the end of the scope.

    $builder->connect(‘/*’, [‘controller’ => ‘React’, ‘action’ => ’embed’]);

    Maybe there’s a better way to do it, but it worked for me.



Submit a Comment

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

The reCAPTCHA verification period has expired. Please reload the page.

You May Also Like…