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 app
  • configureBabel() — 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 the es2015 and stage-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

A sample directory structure using React on Rails for a React App named ‘ApplicationApp’

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.