limenius/react-bundle
Symfony bundle for rendering React components, with optional server-side rendering via Node or V8Js. It integrates React with Twig and Symfony's asset system, adds a Twig render function, and supports hot module replacement during development for a smooth frontend workflow.
First, calibrate expectations: limenius/react-bundle is a Symfony bundle, not a Laravel package. v5.1.0 adds Symfony 6 compatibility, which makes it viable on a modern PHP 8.1+ Symfony stack — but it still ships no service provider, facade, Blade directive, or config/*.php. composer require limenius/react-bundle inside a Laravel app will pull Symfony components that Laravel already provides under different contracts, and nothing will auto-register. A Laravel developer therefore has two realistic on-ramps:
composer require limenius/react-bundle, register Limenius\ReactBundle\LimeniusReactBundle in config/bundles.php, add a config/packages/limenius_react.yaml with default_rendering: client_side to start, run php bin/console assets:install, and use {{ react_component('Hello', {props: {name: 'world'}}) }} in a Twig template. Load the bundle's react_ujs-style client script (or your own mount script) to hydrate data-react-class / data-react-props wrapper divs.POST /render {component, props}, a Laravel ReactRenderService calling it, and a Blade component that emits the same data-react-class/data-react-props wrapper plus a small Vite-mounted hydrate script. Or run the Symfony app as an SSR-only sidecar and call it from Laravel.Where to look first in the repo: Twig/ReactExtension.php for the react_component signature and options, Renderer/ReactRenderer.php for the transport (external Node server vs. php_exec_js/v8js), DependencyInjection/Configuration.php for the real config keys, and Resources/public/js/react_ujs.js for the client-side mount contract. The README is the fastest path to a working Twig example.
The first useful use case is an SEO/first-paint-critical island: SSR a product card, comment thread, or search widget into an otherwise server-rendered page, then hydrate on the client. Start with client_side, flip individual components to both once the Node renderer is healthy.
default_rendering: client_side so the Node dependency is not on the hot path, then opt into server rendering per component via the options argument to react_component (rendering: 'both' / 'server_side'). Treat SSR as a deliberate cost, not a default.react_component returns an object whose getContent() (markup) and getProps() (serialized props) you can consume separately — useful for embedding JSON in a <script> tag for large payloads or for wrapping markup in your own layout.php_exec_js/v8js for local experiments only — it needs the PECL v8js extension and a Node API subset that will bite you.ReactRenderService using Http::timeout(2)->post($rendererUrl, [...]) with a circuit breaker and a fallback to client-only markup; a Blade component <x-react component="ProductCard" :props="$props" :ssr="true" /> emitting the wrapper div; a Vite entry that reads the data attributes and calls createRoot(...).render() or hydrateRoot. This mirrors exactly what the bundle's Twig extension + react_ujs do./render/v3) and restart the Node pool on deploy so stale SSR bundles never serve old markup.php artisan vendor:publish to produce anything useful.react_ujs-style script knows about createRoot/hydrateRoot — if not, write your own mount file rather than patching vendor code.window, document, localStorage, matchMedia, and layout measurements don't exist server-side. Guard them behind useEffect or typeof window !== 'undefined'. This is the number-one runtime failure with any PHP-driven React SSR, including this one.Date.now(), and locale-dependent formatting are common culprits.<script type="application/json"> and pass an ID instead.How can I help you explore Laravel packages today?