Symfony with React.js frontend Integration
The Internet brave new World
Post by Pedro Resende on the 29 of October of 2016 at 20:22
Tags: React.jsMVCSymfonyReactWebpackAzure
For the last three months, I've been working for a customer doing a migration from Wordpress to a custom solutions with React.js + Azure Microservices.
I have to say that at the beginning, React.js seemed to be a bit strange, however, today, I believe it's a perfect candidate to integrate with Symfony.
I'll explain a possible integration, this is only and example, if any of you believe there is a better approach, please leave a comment.
Let's start by installing Symfony
$ symfony new reactintegration
Now that we have a symfony installation, let's begin with the part of react.js integration
$ cd reactintegration
$ npm init .
Now that we have a npm project at the root of our Symfony installation, lets install the needed packages
$ npm install --save react react-dom babel-loader babel-core babel-preset-es2015 babel-preset-react react-hot-loader react-router webpack jquery
Let's start by creating a babel presets for react and ES2015. This will allow to write ES2015 and React.js JSX into current javascript
$ vi .babelrc
Add to the file
{
"presets" : ["es2015", "react"]
}
Edit package.js file and add the following on the "scripts" section
"scripts": {
"build": "webpack",
"watch": "webpack --watch",
},
Now, lets create our webpack.config.js
$ vi webpack.config.js
Add the following inside
var webpack = require('webpack');
var path = require('path');
var BUILD_DIR = path.resolve(__dirname, 'web');
var APP_DIR = path.resolve(__dirname, 'react');
var config = {
entry: [
APP_DIR + '/app.jsx',
],
output: {
path: BUILD_DIR,
filename: 'app.bundle.js',
},
module: {
loaders: [{
test: require.resolve("jquery"),
loader: "imports?jQuery=jquery"
}, {
test: /.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2015', 'react']
}
}, {
test: /\.css$/,
loaders: ['style', 'css', 'less']
}]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
}),
],
};
module.exports = config;
Now, lets create our React.js source
$ mkdir react
$ vi app.jsx
I'll add some demonstration code, that will allow you to see this example in action
import React from 'react';
import {render} from 'react-dom';
import {
Router,
Route,
IndexRoute,
Link,
browserHistory,
Redirect,
IndexRedirect
} from 'react-router';
var Homepage = React.createClass({
render: function() {
return (
HEllo World
)
}
});
var Homepage2 = React.createClass({
render: function() {
return (
HEllo World2
)
}
});
var Homepage404 = React.createClass({
render: function() {
return (
404
)
}
});
render((
<Router history={browserHistory}>
<Route path="/">
<IndexRoute component={Homepage}/>
<Route path="hi" component={Homepage2}/>
<Route path="*" component={Homepage404}/>
</Route>
</Router>
), document.getElementById('app'));
We're almost there, now we all need to do is add the app.bundle.js to our Symfony part
$ vi app/Resources/default/index.html.twig
Replace all the content with
{% extends 'base.html.twig' %}
{% block body %}
<div id="app"></div>
{% endblock %}
{% block javascripts %}
<script src={{ asset('app.bundle.js') }} type="text/javascript"></script>
{% endblock %}
Let's prepare our Controller to deal with React.js
$ vi src/AppBundle/Controller/DefaultController.php
Replace the all the content with
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
/**
* @Route("/api/{slug}", name="API")
*/
public function apiAction(Request $request, $slug = null)
{
// replace this example code with whatever you need
// Symfony Backend integration
}
/**
* @Route("/", name="homepage")
* @Route("/{slug}", name="homepage2")
*/
public function indexAction(Request $request, $slug = null)
{
// replace this example code with whatever you need
return $this->render('default/index.html.twig', [
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..').DIRECTORY_SEPARATOR,
]);
}
}
Now, eveytime you access to "/" our "/{slug}" it will used react-router, the "/api/{slug}" it will use Symfony
To end, we need to "compile" react.js into our app.bundle.js, to do that run the following command
$ npm run watch
This way, everytime you make a change to app.jsx it will re-create a new app.bundle.js file
Now start symfony's server by running
$ php bin/console server:run
and access with your browser to
http://localhost:8080/
You'll see "HEllo World", if you try to access "/lsakdjjksda" you'll be reddirect to the 404 React.js route but if you access to "/api/" the response will be from symfony.
That is it, please let me know if you propose a different approach, the code is available at this link.