SSR React with Symfonyfor the Strong of Spirit ⚔ Workshop React & React Bundle Theory
About the React Bundle.
Set up We have prepared a new Symfony Project with some setup already done for you. Let's check it out:
git clone https://github.com/Limenius/workshop-symfony-react.git
git checkout 04-start-react
And install composer and npm dependencies
If we start our PHP server:
php -S 127.0.0.1:8080 -t public
We should see that we have the following URLS available:
Which will be HTML pages that hold a list of James Bond movies and detailed views for every movie.
And these ones:
Which are a JSON api that will allow us to GET the same resources (list of movies and individual Bond movies).
React & React on Rails Let's create a simple client side React App. Let's start by installing the packages we are going to use:
npm i --save react react-dom react-on-rails
npm i --save-dev babel-preset-react
And then the React Bundle, that will provide us a Twig extension that we can use to render components in client and server side.
composer require limenius/react-bundle
Let's configure the bundle initially for client side rendering only first. Create config/packages/limenius_react.yaml
with this content:
1
2
limenius_react :
default_rendering : "client_side"
And let's create our first React component, in assets/js/MovieComponent.js
1
2
3
4
5
6
7
8
9
10
11
12
import React from "react" ;
const MovieComponent = ( { movie } ) => (
< div className = " movie" >
< div className = " movie-image" >
< img width = " 250" src = { `/images/ ${ movie. image} ` } />
</ div>
< div className = " movie-name" > { movie. name} </ div>
</ div>
) ;
export default MovieComponent;
And a listing of movies that uses this component in assets/js/MovieList.js
1
2
3
4
5
6
7
8
9
10
import React from "react" ;
import MovieComponent from "./MovieComponent" ;
export default class Movies extends React. Component {
render ( ) {
return this . props. movies. map ( movie => (
< MovieComponent key = { movie. slug} movie = { movie} />
) ) ;
}
}
To use a root component, we need to register it using React on Rails. Create this entry point in assets/app.js
1
2
3
4
5
import ReactOnRails from "react-on-rails" ;
import MovieList from "./MovieList" ;
require ( "../css/app.css" ) ;
ReactOnRails. register ( { MovieList } ) ;
Now we can use it from Twig, rewrite the template in templates/movies/list.html.twig
:
1
2
3
4
5
{% extends ' base.html.twig' %}
{% block body %}
{{ react_component ( ' MovieList' , { ' props' : props } ) }}
{% endblock %}
There is only one thing left. Enable support for React (a babel preset) in encore. Edit webpack.config.js
so it reads:
1
2
3
4
5
6
7
8
9
10
11
12
var Encore = require ( "@symfony/webpack-encore" ) ;
Encore. setOutputPath ( "public/build/" )
. setPublicPath ( "/build" )
. addEntry ( "app" , "./assets/js/app.js" )
. cleanupOutputBeforeBuild ( )
. enableBuildNotifications ( )
. enableSourceMaps ( ! Encore. isProduction ( ) )
. enableVersioning ( Encore. isProduction ( ) )
. enableReactPreset ( ) ;
module. exports = Encore. getWebpackConfig ( ) ;
(We have added .enableReactPreset()
at the end of the configuration.)
With this, if we restart webpack encore it should work. Stop it and run this:
And we point our browser to localhost:8080/
. We should see a list of Bond movies
Exercise
Can you write a component for the movie detail page and make it render?
You will need a React component in assets/js/MovieDetail.js
Render it from Twig in templates/movies/detail.html.twig
...and enable it in:
assets/js/app.js
Got lost?
The code up to this point is in the tag 04-client-side
of the repository https://github.com/Limenius/workshop-symfony-react.git
This means:
git reset HEAD --hard
(To discard your changes)
And then:
git checkout 04-client-side
Enable Server Side Rendering What do we need to enable SSR?
Only few things, really.
First, we need to create a new webpack config in webpack.config.ssr.js
1
2
3
4
5
6
7
8
var Encore = require ( "@symfony/webpack-encore" ) ;
Encore. setOutputPath ( "var/webpack/" )
. setPublicPath ( "/" )
. addEntry ( "app" , "./assets/js/app.js" )
. enableReactPreset ( ) ;
module. exports = Encore. getWebpackConfig ( ) ;
And now we need to configure React Bundle to handle it. Edit config/packages/limenius_react.yaml
:
1
2
3
4
5
6
7
8
limenius_react :
default_rendering : "both"
serverside_rendering :
trace : true
fail-loud : true
mode : "phpexecjs"
server_bundle_path : "%kernel.project_dir%/var/webpack/app.js"
And now open a new terminal and run:
npx encore dev --watch --config webpack.config.ssr.js
Open one of our pages. It should look the same (after all, that is the point).
Exercise
Open the source code of the page and see if you can find the HTML code of our server side component.
Now change the configuration of config/packages/limenius_react.yaml
of the option default_rendering
from both
to client_side
and afterwards to server_side
Can you spot the differences?
Got lost?
The code up to this point is in the tag 04-ssr
of the repository https://github.com/Limenius/workshop-symfony-react.git
This means:
git reset HEAD --hard
(To discard your changes)
And then:
git checkout 04-ssr
Go to the next section