SSR React with Symfonyfor the Strong of Spirit Workshop

Webpack Encore

Theory
Let's talk about Webpack Encore.

A Symfony Project

Create a new Symfony Project with:
composer create-project symfony/website-skeleton sfreact
Let's add a first controller in src/Controller/SecretAgentController.php with this code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

namespace AppController;

use SymfonyComponentHttpFoundationResponse;

class SecretAgentController
{
    public function code()
    {
        return new Response(
            '<html><body>Agent code: 007</body></html>'
        );
    }
}
Note that we aren't doing anything with JavaScript yet.
Now, activate your route adding this code toconfig/routes.yml
1
2
3
secret-agent:
    path: /secret-agent
    controller: AppControllerSecretAgentController::code
Tip
Careful, don't add your routes to config/packages/routing.yaml. That file contains the configuration of the routing package, not the definition of routes.
And run the server:
php -S 127.0.0.1:8080 -t public
Open in your browserhttp://localhost:8080/secret-agentTo verify that your Symfony installation is working before adding more pieces to it.

Webpack Encore

Let's install Encore and PhpExecJs
composer require webpack-encore
composer require nacmartin/phpexecjs
npm install
npm install --save left-pad
And write an encore config in webpack.config.ssr.js
1
2
3
4
5
6
7
var Encore = require("@symfony/webpack-encore");

Encore.setOutputPath("var/webpack/")
  .setPublicPath("/")
  .addEntry("server-bundle", "./assets/js/app.js");

module.exports = Encore.getWebpackConfig();
Tip
You will see that there is already a webpack.config.js already. Don't touch it for now. It makes sense to have two different Webpack configurations for the JS code that will be run in your client and in your server.
And place the code we stole to the MI6 unto into assets/js/app.js
1
2
3
4
5
6
7
8
9
import leftPad from "left-pad";

var sum = (a, b) => a + b;

var makeSecretAgent = code => leftPad(code, 3, 0);

var sumSecretAgent = (a, b) => makeSecretAgent(sum(a, b));

global.sumSecretAgent = sumSecretAgent;
Note that this is not the pipeline version. It would be possible to use pipeline in encore, but let's be conservative.
Let's try to generate a SSR bundle with Encore:
Run:
npx encore dev --config webpack.config.ssr.js --watch
And verify that a file var/webpack/server-bundle.js has been created.
Let's adapt our controller to use this JS code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php

namespace AppController;

use SymfonyComponentHttpFoundationResponse;
use NacmartinPhpExecJsPhpExecJs;

class SecretAgentController
{
    public function code($ssrBundleLocation)
    {

        $phpexecjs = new PhpExecJs();

        $phpexecjs->createContextFromFile($ssrBundleLocation);

        $variant = "sumSecretAgent(3, 4);";

        $result = $phpexecjs->evalJs($variant);

        return new Response(
            '<html><body>Agent code: '.$result.'</body></html>'
        );
    }
}
Since we are injecting the parameter $ssrBundleLocation we need to define what it means in config/services.yml
1
2
3
4
5
services:
  _defaults:
    #...
    bind:
      $ssrBundleLocation: "%kernel.project_dir%/var/webpack/server-bundle.js"
With you we should be ready to openhttp://localhost:8080/secret-agentand have all the pieces working together.