PHP with React JS: Combined
Disclaimer: I am just a couple of hours reactJS old (by the time I was writing this), yes that’s how long I have been using reactJS and this here is my first project with reactJS, so obviously there will be things that I have missed or could have done them better.
Backstory
I have been working as a web developer for about five years now. Although I am familiar with other tools like Node.js, my preferred stack has always revolved around PHP due to its ease of use without requiring additional configurations or installations. This stack typically involves HTML, CSS, and JS, complemented by a few other minimal UI frameworks and libraries such as Bootstrap and jQuery.
Since the beginning of this year (2024), I have been actively job hunting (still no luck 😀), which prompted me to broaden my skill set. While browsing and exploring new technologies, I stumbled upon ReactJS. After trying it with Node.js and finding it promising, I, as a hardcore PHP fan, had two questions: Does ReactJS work with PHP, and if so, how?
The answer to the first question is obviously yes. However, concerning the second question, it’s generally agreed upon that to integrate ReactJS with PHP, one needs to utilize APIs. In essence, this entails having two separate app implementations for a single application: one to handle API requests and the other for the front end. While this approach is effective, it requires both time and resources.
After a few minutes of searching and brainstorming, I thought of an alternative solution. By utilizing ReactJS alongside the Blade template, one can employ ReactJS in a manner akin to directly using vanilla tools like HTML, CSS, and JS. Let's see how this method works out.
1. Requirement
Blade template is required, if you’re using a framework like Laravel, the Blade template engine is shipped with it, if you’re using Symfony here’s a guide you can use to integrate the Blade template engine with your Symfony app, now to those using core php without a framework I’d suggest you installjenssegers/blade
stand-alone Blade template and follow the documentation to set it up
composer require jenssegers/blade
As for me I already have my very own basic implementation of an MVC framework which has some initial tools pre-installed including the Blade template engine.
ReactJS and Babel Modules (CDN)
<!-- React CDN -->
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- Babel CDN for JSX -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
2. Hello world
It is commonly known that a simple reactJS app would look something like this if we are going to use the CDNs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple React App</title>
<!-- React CDN -->
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- Babel CDN for JSX -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const App = () => {
return (
<h1>Hello World</h1>
);
};
// Render the component to the DOM
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
</script>
</body>
</html>
Blade TE allows us to place and reuse components neatly and conveniently, hence we’ll have to define a file organization
📁 Views/
├── 📁 layouts/
│ ├── 🌐 app.blade.php
│ ├── 📁 components/
│ │ ├── ⚛ global.blade.jsx
│ │ ├── ⚛ local.blade.jsx
├── ⚛ home.blade.jsx
Please note that if you examine my files, some of them have the extension .blade.jsx
. In your case, to allow the compilation of JSX files to be compiled as views, one will have to add support for the extension initializing the Blade template engine
$blade->addExtension('blade.jsx', 'blade');
mainFile.blade.jsx
Structure
@extends('layouts.app')
@section('components')
// include/declare new local components from here
@endsection
@section('content')
// JSX content falls in here
@endsection
Since we’ve declared that our main app files will be extending layouts/app.blade.php
we’ll have to make sure that the app
is compatible with any other file extending it, therefore app.blade.php
should look something like this.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple React App</title>
<!-- React CDN -->
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- Babel CDN for JSX -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
@yield('components') // contents of section 'components'
const App = () => {
return (
@yield('content') // contents of section 'content'
);
};
// Render the component to the DOM
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
</script>
</body>
</html>
As you can see that now our app.blade.php
can yield and place the sections of the extending file in their respective areas, let’s try populating the ‘content’ section of home.blade.jsx
...
@section('content')
<div>
<h1>Hedy Lamarr's Todos</h1>
<img
src="https://cdn-icons-png.flaticon.com/256/6619/6619606.png"
alt="Hedy Lamarr"
className="photo"
/>
<ul>
<li>Invent new traffic lights</li>
<li>Rehearse a movie scene</li>
<li>Improve the spectrum technology</li>
</ul>
</div>
@endsection
This should give us something like
Something about Reusable components, if you review our directory structure and our file skeleton you notice 3 things
layouts/components/global.blade.jsx
: This file will have all the components that we need to reuse across our applicationlayouts/components/local.blade.jsx
: This file will restore components that are only reusable in certain files, you can add your components to thecomponents
directorysection('components')
: Can be used to include other components or declare its own components to be used in that specific file.
Thanks to Blade’s @include
directive we can use it to call and include other components from anywhere we need, let’s start with the header, we’ll declare this one inglobal.blade.jsx
located in layout components
... global.blade.jsx
const Header = () => {
return (
<header>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</header>
)
}
// other global components
Let’s load the global
components to our application root file layouts/app
... app.blade.php
<script type="text/babel">
@include('layouts.components.global') // here it is
@yield('components')
const App = () => {
return (
<div className="appContainer">
<Header /> // use your component just like that 😊
@yield('content')
</div>
);
};
...
</script>
That should do the trick with loading the Header component and the content section, but let’s make it interesting, let’s assume while rendering our view, we passed some data through it.
view (
'home', [ 'todos' => [
'Go to the store',
'Finish the tutorial',
'Clean the house'
]]
);
# now we've got a variable array $todos to work with
@extends('layouts.app')
@section('components')
var owner = 'Hedy Lamarr';
// called and parsed our variable to JSON (for option 1)
var initialTodos = JSON.parse('{!! json_encode($todos) !!}');
const TodoApp= () => {
return (
<ul>
// option 1
{initialTodos.map((todo, index) => (
<li key={index}>{todo}</li>
))}
/** Option 2
* directly use blade directives like @php, @foreach, etc
* to complete the render or you component with PHP
*/
</ul>
);
};
@endsection
@section('content')
<div>
<h1> {owner} </h1>
<img
src="https://cdn-icons-png.flaticon.com/256/6619/6619606.png"
className="photo"
/>
<TodoApp />
</div>
@endsection
NOTE: make sure you’re careful with the curly brackets
{ content }
: That’s for reactJS and will be parsed in the browser{{ content }}
,{!! content !!}
: That will be parsed during processing on the server
Let’s make things more interesting, by adding a form that would allow us to change the state of our list
... home.blade.jsx
@section('components')
var owner = 'Hedy Lamarr';
var initialTodos = JSON.parse('{!! json_encode($todos) !!}');
const TodoApp = () => {
const [todos, setTodos] = React.useState(initialTodos);
// new component to process our todo form
const TodoFormSubmit = (e) => {
e.preventDefault();
const newTodo = e.target.elements.todo.value;
setTodos([...todos, newTodo]); // update list
e.target.reset(); // Reset the form after submission
};
return (
<div>
<ul id="toDoList">
{todos.map((todo, index) => (
<li key={index}>{todo}</li>
))}
</ul>
<form onSubmit={TodoFormSubmit}>
<input type="text" name="todo" placeholder="Add a todo" />
<button type="submit">Add</button>
</form>
</div>
);
};
@endsection
That should give us something like this
I believe that wraps up our tutorials on integrating ReactJS with PHP without completely separating the application layers.
You can find the code for this tutorial here.
Additionally, I’m currently developing a PHP+ReactJS (mimosa) framework. If you find it helpful, be sure to keep an eye on the GitHub repository @ibnsultan/mimosa.