DEV Community

Cover image for Laravel + React
Joe Czubiak
Joe Czubiak

Posted on • Originally published at joeczubiak.com on

Laravel + React

This tutorial will show you how to use React with Laravel in a way that lets you sprinkle React into a legacy Laravel codebase and blade templates. We will not be creating an SPA or using Create React App.

You can view and download the full sample project.

https://github.com/jcz530/laravel-plus-react

After going through this guide...

  • We'll be able to add React components into blade files.
  • We'll have reusable components that can be combined to make complex components.
  • We'll use webpack (Laravel Mix) to build our files.
  • We will not have an SPA.
  • React will not be served with SSR (Server Side Rendering).
  • We will not be able to use the components as inline components like is popular with Vue.

Background

I was inspired to write this guide because recently, I added React into a legacy project of mine, and I didn't want to rewrite the whole project to turn it into a React SPA. Instead, I wanted to reap the benefits of writing new React components that I could start sprinkling into my project right away.

There are a lot of ways to get React to load and render components, and this is simply the method I choose when working on my project. I'll walk you through how and why I chose this setup.

First thing's first, navigate to your existing or new Laravel project.

Install Dependencies

npm i react react-dom

Folder Structure

In the /resources/js/ folder, we'll add a new folder where all of our React files will live. We want to keep these files all together and not mixed in with other JS files. This will keep the project organized, make some of the webpack setup easier, and allow for the use of other technologies.

In my case, I created a source folder for all of my React files at /resources/js/src/.

I have the following folders in the src folder.

  • /src/components
  • /src/hooks
  • /src/layouts
  • /src/pages

Your exact folders may vary depending on your needs and organizational style, but this could be a good place to start.

Laravel Mix - Webpack setup

Aliases

This step is optional, but I think it makes the project a lot easier and cleaner to work with. Defining aliases in the webpack configs will allow you to refer to your files without needing to know where in the file path you are.

For example, if you want to refer to your theme file from a component deep in the folder structure, without aliases, you might write

import theme from '../../../themes/theme.js'

With aliases, you would simply write

import theme from 'themes/theme.js'

To use aliases, you'll need to add them to your mix file webpack.mix.js.

mix.webpackConfig({
    resolve: {
        alias: {
            //adding react and react-dom may not be necessary for you but it did fix some issues in my setup.
            'react' : path.resolve('node_modules/react'),
            'react-dom' : path.resolve('node_modules/react-dom'),

            'components' : path.resolve('resources/js/src/components'),
            'pages' : path.resolve('resources/js/src/pages'),
            'themes' : path.resolve('resources/js/src/themes'),
            'layouts' : path.resolve('resources/js/src/layouts'),
            'hooks' : path.resolve('resources/js/src/hooks'),
        },
    },
});
Enter fullscreen mode Exit fullscreen mode
webpack.mix.js

Bundle and Extract React

After you've added your aliases, you'll need to tell webpack to bundle your files and extract libraries. In the same webpack.mix.js file, add the following line. Notice that we're using mix.react and we are using app.js. If your app.js file already has legacy code, you could create a new app file for the React components.

mix.react('resources/js/app.js', 'public/js').extract(['react', 'react-dom']);
Enter fullscreen mode Exit fullscreen mode
webpack.mix.js

Rendering the components

This is where things get tricky.

Even though we aren't building an SPA, we still want to be able to build complex components that reuse multiple components. We're going to be mixing React components into blade files, and it would be great if we could retain some of the JS feel for the components so that we know we're referring to a React component, and it's not just a random div with an id.

Instead of referring to components as <div id="MyComponent" />

We are instead going to use <MyComponent />.

This isn't valid html, so if you want to use the id method, all you'll have to do is uncomment one of the lines in the ReactRenderer.js file coming up.

Create a simple component

We need a simple component to test with, and this is about as simple as they get.

Create a new file with the following code in src/components/MySimpleComponent.js.

import React from 'react';

export default function MySimpleComponent(props) {

  return (
    <>
        <h2>This was loaded from a React component.</h2>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode
components/MySimpleComponent.js

Set up app.js

Next, we need to set up the app.js file. These are the lines that you'll need to add to the app.js file.

require('./bootstrap')
import React from 'react'
import ReactRenderer from './src/ReactRenderer'

import MySimpleComponent from 'components/MySimpleComponent'

const components = [
  {
    name: "MySimpleComponent",
    component: <MySimpleComponent />,
  },
]

new ReactRenderer(components).renderAll()

Enter fullscreen mode Exit fullscreen mode
app.js

A little explanation.

In our app.js file we will import any components that we want to use within the blade files and add them to an array. We'll use the 'name' element to find all the references to the component in the blade files, and we'll use the 'component' element to render it.

Next we need to add the ReactRenderer.js file.

import React from 'react';
import ReactDOM from 'react-dom';

export default class ReactRenderer {

  constructor(components) {
    this.components = components;
  }

  renderAll() {

    for (let componentIndex = 0; componentIndex < this.components.length; componentIndex++) {

      // Use this to render React components in divs using the id. Ex, <div id="MySimpleComponent"></div>
      // let container = document.getElementById(this.components[componentIndex].name);

      // Use this to render React components using the name as the tag. Ex, <MySimpleComponent></MySimpleComponent>
      let containers = document.getElementsByTagName(this.components[componentIndex].name)

      if (containers && containers.length > 0) {

        for (let i = containers.length - 1; i >= 0; i--) {
          let props = this.getPropsFromAttributes(containers[i]);
          let element = this.components[componentIndex].component;

          if (props !== null) {
            element = React.cloneElement(
              element,
              props
            )
          }

          ReactDOM.render(element, containers[i]);
        }
      }
    }
  }

  // Turns the dom element's attributes into an object to use as props.
  getPropsFromAttributes(container) {
    let props = {};
    if (container.attributes.length > 0) {
      for (let attributeIndex = 0; attributeIndex < container.attributes.length; attributeIndex++) {
        let attribute = container.attributes[attributeIndex];
        if (this.hasJsonStructure(attribute.value)) {
          props[attribute.name] = JSON.parse(attribute.value);
        } else {
          props[attribute.name] = attribute.value;
        }
      }
      return props;
    }
    return null;
  }

  hasJsonStructure(str) {
    if (typeof str !== 'string')
      return false;
    try {
      const result = JSON.parse(str);
      const type = Object.prototype.toString.call(result);
      return type === '[object Object]' || type === '[object Array]';
    } catch (err) {
      return false;
    }
  }

}

Enter fullscreen mode Exit fullscreen mode
ReactRenderer.js

You can read through the code to more fully understand what is happening. At its core, it's just finding all DOM elements that match your components and rendering them with any props included as well.

Put it to work

Now that we have everything in place, we can start to build more components and add them to blade files.

Here are some examples of adding it to blade files.

...
<MySimpleComponent></MySimpleComponent>

@guest
<MySecondComponent
    title="This is using blade's {{'@'}}guest helper to show to 'Guests' only"
/>
@endguest

@auth
{{-- Remember to use "json_encode" to pass in objects --}}
<MySecondComponent
    title="This is showing to authed users"
    user="{{ json_encode(auth()->user()) }}"
/>
@endauth
...
Enter fullscreen mode Exit fullscreen mode
app.blade.php

In the source code for this tutorial, I've also included a second component that accepts a title prop. This code is a snippet from the app.blade.php file in the source code.

If you download and run the sample project, you will get something that looks like this.

Laravel + React

I encourage you to download the repo, explore, and make modifications to test it out. https://github.com/jcz530/laravel-plus-react

Discussion (1)

Collapse
timbogdanov profile image
Tim Bogdanov

Why do you need the react renderer? Is there anyway around this, it just seams hacky. Is there a folder structure that you would recommend for react when using it with Laravel?