How to use React with Symfony 4
Introduction
Symfony is a lightweight and flexible framework built in PHP. It comes with enough tooling and optional bundles that you can build basically any kind of web application. One strength of Symfony is the ability to build out a robust API backend to integrate with a javascript front end. This article will give steps on how to set up and run a React application on a Symfony backend, which allows you to use Symfony’s robust database management and security tools with the interactivity of a front end framework like React.
Tools
The following are required to follow along with this article
- Symfony — The application framework
- React — Front end framework
- Webpack Encore — Build tool for our javascript and styles
- Yarn — Package manager for javascript dependencies
- React Bundle — Provides server-side rendering and twig paramter passing
- React on Rails — Required for React Bundle, makes it easier to register React components in Javascript
Step 1 — Gather our Tools
This article won’t cover how to start a Symfony application, but there are a ton of great resources out there already including the always handy Symfony Docs.
Once the Symfony project skeleton is created check to make sure Webpack Encore is installed. Check package.json
to see if it’s installed and not run
yarn add @symfony/webpack-encore --dev
This will use yarn to install webpack encore and make it a dev dependency.
The package manager yarn can be used to install all of the tools needed to create a React app and compile it down to javascript using webpack. Here’s an example of what the devDependencies
block in package.json
can look like after everything is installed (version numbers don’t need to match exactly).
"@symfony/webpack-encore": "^0.19.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-on-rails": "^11.0.8",
"react-transition-group": "^2.3.1"
Depending on the scope of a project there may need to be more Symfony dependencies, but the only immediate requirement is
composer require limenius/react-bundle
This should provide a project all the tools needed to build and run a Symfony application with a React front end. Next we’ll address configuring our tools.
Step 2 — Configuration
Now that we have the tools, we need to get them set up and configured properly to do their jobs.
React Bundle
To start we need to configure React Bundle to render our React components. An advantage immediately gained by using this bundle is server-side rendering. This means the server will do the work of compiling javascript and provide rendered HTML as soon as the page loads, which means no additional work for the browser. After the initial load React Bundle will then hand off any changes to the javascript downloaded by the browser on the web page.
After installing it with composer the configuration for React Bundle can be found at config/packages/limenius_react.yml
. It should already look close to this
limenius_react:
default_rendering: "both"
serverside_rendering:
fail_loud: false
trace: false
mode: "phpexecjs"
server_bundle_path: "%kernel.project_dir%/var/webpack/server-bundle.js"
server_socket_path: null
An explanation for each of these options can be found in the documentation for React Bundle. The main focus here is theserver_bundle_path
option. This is the path where the compiled server-side javascript file can be located. To keep the rendering load on the server as small as possible (and make sure pages aren’t slow when loading) this file should be as small as possible and only include the javascript needed to initially render a page. This file is generated by Webpack Encore, which is our next topic.
Webpack Encore
Webpack Encore is a build tool created by the team at Symfony. It’s built on top of Webpack and allows for a quicker setup along with extra tools and options specifically created for Symfony.
The configuration for Webpack Encore can be found in webpack.config.js
at the root directory of a Symfony project. A base template should be automatically created when Encore is installed, and should look similar to this
// webpack.config.js
let Encore = require('@symfony/webpack-encore');Encore
// the project directory where compiled assets will be stored
.setOutputPath('public/build/')
// the public path used by the web server to access the previous directory
.setPublicPath('/build')
// the public path you will use in Symfony's asset() function - e.g. asset('build/some_file.js')
//.setManifestKeyPrefix('build/').cleanupOutputBeforeBuild()
.enableSourceMaps(!Encore.isProduction())// the following line enables hashed filenames (e.g. app.abc123.css)
.enableVersioning(Encore.isProduction())
// allow sass/scss files to be processed
.enableSassLoader()// React Pages Javascript
.addEntry('js/app', './assets/js/React/App/Startup/registration.js')// Add style entry
.addStyleEntry('css/app', './assets/scss/app.scss')// Add react preset
.enableReactPreset().configureBabel(function(babelConfig) {
// add additional presets
babelConfig.presets.push('es2015');
babelConfig.presets.push('stage-0');
}).enableBuildNotifications().enablePostCssLoader()// create hashed filenames (e.g. app.abc123.css)
//.enableVersioning()
;// export the final configuration
module.exports = Encore.getWebpackConfig();
Each of these options are fully defined in the documentation for Webpack Encore, but the following options apply directly to building a React application.
enableReactPreset()
— This option let’s Webpack Encore know that the application is going to be processing JSX in a React appconfigureBabel()
— This is where specific requirements can be set for Babel. As of this writing to ensure an app can properly compile all of the ES6 features that are suggested to use with React thees2015
andstage-0
presets will need to be enabled.
If there are other javascript files needed for an app that don’t relate to React they’ll likely end up in this file as well. At the end of the site’s development this configuration file may also contain settings for autoprefixing, compiling SASS and versioning assets.
Since a majority of this code isn’t necessary for the server rendered javascript file that creates a page’s initial structure, a separate webpack encore config file should be created that only holds the javascript necessary to render a page.
The file can be named anything, but for this example it is named webpack.config.serverside.js
An example server-side webpack encore config file can look like
var Encore = require('@symfony/webpack-encore');Encore
// directory where all compiled assets will be stored
.setOutputPath('var/webpack/')
// what's the public path to this directory (relative to your project's document root dir)
.setPublicPath('/')
// empty the outputPath dir before each build
.cleanupOutputBeforeBuild()
// will output as app/Resources/webpack/server-bundle.js
.addEntry('server-bundle','./assets/js/React/ServerRender/registration.js')
// Add react preset
.enableReactPreset().configureBabel(function (babelConfig) {
// add additional presets
babelConfig.presets.push('es2015');
babelConfig.presets.push('stage-0');// no plugins are added by default, but you can add some
// babelConfig.plugins.push('styled-jsx/babel');
}).enableSourceMaps(!Encore.isProduction());// export the final configuration
module.exports = Encore.getWebpackConfig()
In this file the build path is set to var/wepack
This tells webpack to put all compiled javascript files in the var/webpack
directory and matches the configuration set up for the Symfony React Bundle.
React On Rails
React on Rails makes it easier to register and bundle React apps in separate files. It’s also required to leverage server-side rendering in twig templates. The only extra step required to implement React on Rails is to make a registration.js
file for each React app in your project. An example looks like
import ReactOnRails from 'react-on-rails'
import ReactApp from './ReactApp';ReactOnRails.register({ ReactApp });
It can help to organize your React app under one overall directory, with two sub directories named Startup
and Containers
. The Startup
directory will hold the javascript file with the React app class along with a registration.js
. The Containers
directory can be used to hold each of the components used throughout your app. An example of this structure can be seen below
Step 3 — Add React to Twig
The final step is to add the React app to the Symfony twig templates. Depending on how an application is set up it can either have multiple twig templates that each have their own React App or have one large React App that uses something like the React Router to handle page navigation.
Adding React
The Symfony React bundle has a twig shortcut that will automatically add a React app to the page and will also allow for the javascript rendered by the server to be sent on page. This prevents extra loading time after the browser initially renders a page. The twig shortcut to add a React app is
{{ react_component('ReactApp', {'props': {} }) }}
The first parameter for the shortcut is the name of the React app that was registered by React on Rails in registration.js
.
The props
parameter accepts an object that includes any data to pass initially to the React App. The data objects can be set as twig variables configured in the Symfony controller
{{ react_component('ReactApp', {'props': {'data': data, 'data2': data2} }) }}
The data passed in the twig shortcut can be collected in the constructor function of React class registered by React on Rails. The data is passed in a object that can be retrieved from the props
parameter as shown below
constructor(props) {
super();
this.state = {
data: JSON.parse(data), //If JSON data is provided
data2: data2 // For other types of data
}
}
Using the twig shortcut will show the rendered HTML produced by the React App, but to make sure the app continues to run a script tag pointing to the javascript file containing the compiled React code is required. It can be added using normal Symfony/Twig practices as shown below
{% block javascripts %}
{{ parent() }}
<script src="{{ asset('build/js/ReactApp.js') }}" type="application/javascript"></script>
{% endblock %}
After everything is added the React app should be rendered as soon as the page loads with all of it’s initial data and it should be running as expected.
Conclusion
You should now have a functioning React app running on your Symfony site. You can register as many react apps as needed to keep your codebase modular, or you can use Symfony to solely to run the API behind a single page application. If leveraged properly Symfony and React can build an application that meets any requirements you have for a new website.