PHPackages                             pwt777/react-renderer - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. pwt777/react-renderer

ActiveLibrary[Templating &amp; Views](/categories/templating)

pwt777/react-renderer
=====================

Client and Server-side React rendering

v5.1.0(4y ago)081MITPHPPHP &gt;=7.2.0

Since Jan 31Pushed 4y agoCompare

[ Source](https://github.com/pwt777/ReactRenderer)[ Packagist](https://packagist.org/packages/pwt777/react-renderer)[ RSS](/packages/pwt777-react-renderer/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (1)Dependencies (6)Versions (31)Used By (1)

ReactRenderer
=============

[](#reactrenderer)

ReactRenderer lets you implement React.js client and server-side rendering in your PHP projects, allowing the development of universal (isomorphic) applications.

It was previously part of [ReactBundle](https://github.com/Limenius/ReactBundle) but now can be used standalone.

If you wish to use it with Silex, check out @teameh [Silex React Renderer Service Provider](https://github.com/teameh/silex-react-renderer-provider).

Features include:

- Prerender server-side React components for SEO, faster page loading, for users that have disabled JavaScript, or for Progressive Web Applications.
- Twig integration.
- Client-side render will take the server-side rendered DOM, recognize it, and take control over it without rendering again the component until needed.
- Error and debug management for server and client side code.
- Simple integration with Webpack.

[![Build Status](https://camo.githubusercontent.com/692ec405ffed94ee0a9ec17158f87605816a0f951ded5931cd1c6ec7e0896e3f/68747470733a2f2f7472617669732d63692e6f72672f4c696d656e6975732f526561637452656e64657265722e7376673f6272616e63683d6d6173746572)](https://travis-ci.org/Limenius/ReactRenderer)[![Latest Stable Version](https://camo.githubusercontent.com/b355b4ad844b4f35bc52243647c2414a77b61a93a1dfac5c7e8fddd0f0c6b2e2/68747470733a2f2f706f7365722e707567782e6f72672f6c696d656e6975732f72656163742d72656e64657265722f762f737461626c65)](https://packagist.org/packages/limenius/react-renderer)[![Latest Unstable Version](https://camo.githubusercontent.com/2ffd8a5ecd6f2365affe6cc2be95b0c637b206e36fda56164d7c5fb64b83d5a0/68747470733a2f2f706f7365722e707567782e6f72672f6c696d656e6975732f72656163742d72656e64657265722f762f756e737461626c65)](https://packagist.org/packages/limenius/react-renderer)[![License](https://camo.githubusercontent.com/7b3c69d255f5377856cf11c42da93532385755924bfe6a930bc84df016586ccc/68747470733a2f2f706f7365722e707567782e6f72672f6c696d656e6975732f72656163742d72656e64657265722f6c6963656e7365)](https://packagist.org/packages/limenius/react-renderer)

Complete example
----------------

[](#complete-example)

For a complete live example, with a sensible Webpack set up, a sample application to start with and integration in a Symfony Project, check out [Symfony React Sandbox](https://github.com/Limenius/symfony-react-sandbox).

Installation
------------

[](#installation)

ReactRenderer uses Composer, please checkout the [composer website](http://getcomposer.org) in case of doubt about this.

This command will install `ReactRenderer` into your project.

```
$ composer require limenius/react-renderer
```

> ReactRenderer follows the PSR-4 convention names for its classes so you can integrate it with your autoloader.

Usage
-----

[](#usage)

### JavaScript and Webpack set up

[](#javascript-and-webpack-set-up)

In order to use React components you need to register them in your JavaScript. This bundle makes use of the React On Rails npm package to render React Components (don't worry, you don't need to write any Ruby code! ;) ).

Your code exposing a React component would look like this:

```
import ReactOnRails from "react-on-rails";
import RecipesApp from "./RecipesAppServer";

ReactOnRails.register({ RecipesApp });
```

Where RecipesApp is the component we want to register in this example.

Note that it is very likely that you will need separated entry points for your server-side and client-side components, for dealing with things like routing. This is a common issue with any universal (isomorphic) application. Again, see the sandbox for an example of how to deal with this.

If you use server-side rendering, you are also expected to have a Webpack bundle for it, containing React, React on Rails and your JavaScript code that will be used to evaluate your component.

Take a look at [the Webpack configuration in the symfony-react-sandbox](https://github.com/Limenius/symfony-react-sandbox/blob/master/webpack.config.serverside.js) for more information.

### Enable Twig Extension

[](#enable-twig-extension)

First, you need to configure and enable the Twig extension.

```
use Limenius\ReactRenderer\Renderer\PhpExecJsReactRenderer;
use Limenius\ReactRenderer\Twig\ReactRenderExtension;
use Limenius\ReactRenderer\Context\SymfonyContextProvider;

// SymfonyContextProvider provides information about the current request, such as hostname and path
// We need an instance of Symfony\Component\HttpFoundation\RequestStack to use it
$contextProvider = new SymfonyContextProvider($requestStack);
$renderer = new PhpExecJsReactRenderer(__DIR__.'/client/build/server-bundle.js', false, $contextProvider);
$ext = new ReactRenderExtension($renderer, $contextProvider, 'both');

$twig->addExtension(new Twig_Extension_StringLoader());
$twig->addExtension($ext);
```

`ReactRenderExtension` needs as arguments a *renderer* and a string that defines if we are rendering our React components `client_side`, `render_side` or `both`.

The renderer is one of the renders that inherit from [`AbstractReactRenderer`](ReactRenderer/src/Limenius/ReactRenderer/Renderer/AbstractReactRenderer.php).

This library provides currently two renderers:

- `PhpExecJsReactRenderer`: that uses internally [phpexecjs](https://github.com/nacmartin/phpexecjs) to autodetect the best javascript runtime available.
- `ExternalServerReactRenderer`: that relies on a external nodeJs server.

Now you can insert React components in your Twig templates with:

```
{{ react_component('RecipesApp', {'props': props}) }}
```

Where `RecipesApp` is, in this case, the name of our component, and `props` are the props for your component. Props can either be a JSON encoded string or an array.

For instance, a controller action that will produce a valid props could be:

```
/**
 * @Route("/recipes", name="recipes")
 */
public function homeAction(Request $request)
{
    $serializer = $this->get('serializer');
    return $this->render('recipe/home.html.twig', [
        'props' => $serializer->serialize(
            ['recipes' => $this->get('recipe.manager')->findAll()->recipes], 'json')
    ]);
}
```

### Server-side, client-side or both?

[](#server-side-client-side-or-both)

You can choose whether your React components will be rendered only client-side, only server-side or both, either in the configuration as stated above or per Twig tag basis.

If you set the option `rendering` of the Twig call, you can override your config (default is to render both server-side and client-side).

```
{{ react_component('RecipesApp', {'props': props, 'rendering': 'client_side'}) }}
```

Will render the component only client-side, whereas the following code

```
{{ react_component('RecipesApp', {'props': props, 'rendering': 'server_side'}) }}
```

... will render the component only server-side (and as a result the dynamic components won't work).

Or both (default):

```
{{ react_component('RecipesApp', {'props': props, 'rendering': 'both'}) }}
```

You can explore these options by looking at the generated HTML code.

### Debugging

[](#debugging)

One important point when running server-side JavaScript code from PHP is the management of debug messages thrown by `console.log`. ReactRenderer, inspired React on Rails, has means to replay `console.log` messages into the JavaScript console of your browser.

To enable tracing, you can set a config parameter, as stated above, or you can set it in your template in this way:

```
{{ react_component('RecipesApp', {'props': props, 'trace': true}) }}
```

Note that in this case you will probably see a React warning like

*"Warning: render(): Target node has markup rendered by React, but there are unrelated nodes as well. This is most commonly caused by white-space inserted around server-rendered markup."*

This warning is harmless and will go away when you disable trace in production. It means that when rendering the component client-side and comparing with the server-side equivalent, React has found extra characters. Those characters are your debug messages, so don't worry about it.

### Context

[](#context)

This library will provide context about the current request to React components. Your components will receive two arguments on instantiation:

```
const App = (initialProps, context) => {};
```

The Symfony context provider has this implementation:

```
    public function getContext($serverSide)
    {
        $request = $this->requestStack->getCurrentRequest();

        return [
            'serverSide' => $serverSide,
            'href' => $request->getSchemeAndHttpHost().$request->getRequestUri(),
            'location' => $request->getRequestUri(),
            'scheme' => $request->getScheme(),
            'host' => $request->getHost(),
            'port' => $request->getPort(),
            'base' => $request->getBaseUrl(),
            'pathname' => $request->getPathInfo(),
            'search' => $request->getQueryString(),
        ];
    }
```

So you can access these properties in your React components, to get information about the request, and if it has been rendered server side or client side.

### Server-Side modes

[](#server-side-modes)

This library supports two modes of using server-side rendering:

- Using [PhpExecJs](https://github.com/nacmartin/phpexecjs) to auto-detect a JavaScript environment (call node.js via terminal command or use V8Js PHP) and run JavaScript code through it.
- Using an external node.js server ([Example](https://github.com/Limenius/symfony-react-sandbox/blob/master/external-server.js). It will use a dummy server, that knows nothing about your logic to render React for you. Introduces more operational complexity (you have to keep the node server running, which is not a big deal anyways).

Currently, the best option is to use an external server in production, since having [V8js](https://github.com/phpv8/v8js) is rather hard to compile. However, if you can compile it or your distribution/OS has good packages, it is a very good option if you enable caching, as we will see in the next section.

### Redux

[](#redux)

If you're using [Redux](http://redux.js.org/) you could use this library to hydrate your store's:

Use `redux_store` in your Twig file before you render your components depending on your store:

```
{{ redux_store('MySharedReduxStore', initialState ) }}
{{ react_component('RecipesApp') }}
```

`MySharedReduxStore` here is the identifier you're using in your javascript to get the store. The `initialState` can either be a JSON encoded string or an array.

Then, expose your store in your bundle, just like your exposed your components:

```
import ReactOnRails from "react-on-rails";
import RecipesApp from "./RecipesAppServer";
import configureStore from "./store/configureStore";

ReactOnRails.registerStore({ configureStore });
ReactOnRails.register({ RecipesApp });
```

Finally use `ReactOnRails.getStore` where you would have used your the object you passed into `registerStore`.

```
// Get hydrated store
const store = ReactOnRails.getStore("MySharedReduxStore");

return (

);
```

Make sure you use the same identifier here (`MySharedReduxStore`) as you used in your Twig file to set up the store.

You have an example in the [Sandbox](https://github.com/Limenius/symfony-react-sandbox).

### Generator Functions

[](#generator-functions)

Instead of returning a component, you may choose to return an object from your JavaScript code.

One use case for this is to render Title or other meta tags in Server Side Rendering with [React Helmet](https://github.com/nfl/react-helmet). You may want to return the generated HTML of the component along with the title.

```
export default (initialProps, context) => {
  const renderedHtml = {
    componentHtml: renderToString(),
    title: Helmet.renderStatic().title.toString()
  };
  return { renderedHtml };
};
```

In these cases, the primary HTML code that is going to be rendered must be in the key `componentHtml`. You can access the resulting array in Twig:

```
{% set recipes = react_component_array('RecipesApp', {'props': props}) %}
{% block title %}
  {{ recipes.title is defined ? recipes.title | raw : '' }}
{% endblock title %}

{% block body %}
  {{ recipes.componentHtml | raw }}
{% endblock %}
```

There is an example of this in the sandbox.

### Buffering

[](#buffering)

If you set pass `buffered: true` as an option of `react_component` the context and `props` are not immediately included in the template. All this data is buffered and can be inserted right before the closing body tag with `react_flush_buffer`:

```
{{ react_component('RecipesApp', {'props': props, 'buffered': true}) }}
{{ react_flush_buffer() }}
```

This is recommend if you have a lot of `props` and don't want to include them in the first parts of your HTML response. See

(This feature was a flag instead of a named option before 4.0).

### Cache

[](#cache)

There are two types of cache, of very different nature:

- You can cache individual components.
- If you are using the php extension V8 you can cache a snapshot of the V8 virtual machine.

#### Component cache

[](#component-cache)

Sometimes you want to cache a component and don't go through the server-side rendering process again. In that case, you can set the option `cached` to `true`:

`{{ react_component('RecipesApp', {'props': props, 'cached': true}) }}`

You can also set a cache key. This key could be for instance the id of an entity. Well, it is up to you. If you don't set an id, the id will be based on the name of the component, so no matter what props you pass, the component will be cached and rendered with the same representation.

`{{ react_component('RecipesApp', {'props': props, 'cached': true, 'cache_key': "hi_there"}) }}`

To enable/disable the cache globally for your app you need to write this configuration. The default value is disabled, so please enable this feature if you plan to use it.

```
limenius_react:
  serverside_rendering:
    cache:
      enabled: true
```

(This feature was called previously static render before 4.0).

#### V8 cache

[](#v8-cache)

If in your config.prod.yaml or `config/packages/prod/limenius_react.yaml` you add the following configuration, and you have V8js installed, this bundle will be much faster:

```
limenius_react:
  serverside_rendering:
    cache:
      enabled: true
      # name of your app, it is the key of the cache where the v8 snapshot will be stored.
      key: "recipes_app"
```

After the first page render, this will store a snapshot of the JS virtual machine V8js in the cache, so in subsequent visits, your whole JavaScript app doesn't need to be processed again, just the particular component that you want to render.

With the cache enabled, if you change code of your JS app, you will need to clear the cache.

License
-------

[](#license)

This library is under the MIT license. See the complete license in the bundle:

```
LICENSE.md

```

Credits
-------

[](#credits)

ReactRenderer is heavily inspired by the great [React On Rails](https://github.com/shakacode/react_on_rails), and uses its npm package to render React components.

###  Health Score

30

—

LowBetter than 64% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity4

Limited adoption so far

Community18

Small or concentrated contributor base

Maturity69

Established project with proven stability

 Bus Factor1

Top contributor holds 69.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~61 days

Recently: every ~181 days

Total

30

Last Release

1606d ago

Major Versions

v0.14.3 → 1.0.x-dev2017-11-22

v1.0.0 → 2.0.x-dev2017-11-23

v2.3.0 → v3.0.02018-12-06

v2.3.1 → v4.0.02019-10-23

v4.0.2 → v5.0.02019-12-30

PHP version history (2 changes)v0.11.0PHP &gt;=5.5.0

v5.1.0PHP &gt;=7.2.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/a054a912ed7af92d85c1e3fed45691fcc0dd45f87c989681c82c02cc15afa4ae?d=identicon)[pwt777](/maintainers/pwt777)

---

Top Contributors

[![nacmartin](https://avatars.githubusercontent.com/u/154258?v=4)](https://github.com/nacmartin "nacmartin (69 commits)")[![deguif](https://avatars.githubusercontent.com/u/993399?v=4)](https://github.com/deguif "deguif (9 commits)")[![teameh](https://avatars.githubusercontent.com/u/1330668?v=4)](https://github.com/teameh "teameh (6 commits)")[![check24-larserler](https://avatars.githubusercontent.com/u/139362689?v=4)](https://github.com/check24-larserler "check24-larserler (2 commits)")[![Garethp](https://avatars.githubusercontent.com/u/709912?v=4)](https://github.com/Garethp "Garethp (2 commits)")[![jfoucher](https://avatars.githubusercontent.com/u/152015?v=4)](https://github.com/jfoucher "jfoucher (2 commits)")[![VictoriaQ](https://avatars.githubusercontent.com/u/389302?v=4)](https://github.com/VictoriaQ "VictoriaQ (2 commits)")[![jrfnl](https://avatars.githubusercontent.com/u/663378?v=4)](https://github.com/jrfnl "jrfnl (1 commits)")[![gorhawk](https://avatars.githubusercontent.com/u/10365459?v=4)](https://github.com/gorhawk "gorhawk (1 commits)")[![jenskompter](https://avatars.githubusercontent.com/u/110913672?v=4)](https://github.com/jenskompter "jenskompter (1 commits)")[![angelopaolosantos](https://avatars.githubusercontent.com/u/19332518?v=4)](https://github.com/angelopaolosantos "angelopaolosantos (1 commits)")[![mykiwi](https://avatars.githubusercontent.com/u/334432?v=4)](https://github.com/mykiwi "mykiwi (1 commits)")[![ptondereau](https://avatars.githubusercontent.com/u/4287777?v=4)](https://github.com/ptondereau "ptondereau (1 commits)")[![pwt777](https://avatars.githubusercontent.com/u/57768461?v=4)](https://github.com/pwt777 "pwt777 (1 commits)")

---

Tags

twigreactreactjsisomorphic

###  Code Quality

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/pwt777-react-renderer/health.svg)

```
[![Health](https://phpackages.com/badges/pwt777-react-renderer/health.svg)](https://phpackages.com/packages/pwt777-react-renderer)
```

###  Alternatives

[limenius/react-renderer

Client and Server-side React rendering

2411.2M6](/packages/limenius-react-renderer)[twig/extra-bundle

A Symfony bundle for extra Twig extensions

91292.0M315](/packages/twig-extra-bundle)[twig/intl-extra

A Twig extension for Intl

36663.2M221](/packages/twig-intl-extra)[rcrowe/twigbridge

Adds the power of Twig to Laravel

9105.9M50](/packages/rcrowe-twigbridge)[twig/string-extra

A Twig extension for Symfony String

21946.0M133](/packages/twig-string-extra)[twig/cssinliner-extra

A Twig extension to allow inlining CSS

23018.5M55](/packages/twig-cssinliner-extra)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
