Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

React Php V8Js Laravel Package

reactjs/react-php-v8js

react-php-v8js integrates React with PHP by executing JavaScript through the V8Js extension, enabling server-side rendering of React components without Node.js. Use it to add isomorphic React views and pre-rendered markup to PHP applications while keeping rendering in-process.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a thin PHP bridge around the PECL v8js extension, so the package install is the easy half — the extension is the real gate.

1. Confirm the runtime, not the composer package, is available

php -m | grep v8js          # must print "v8js"
php -i | grep v8js         # check v8js.script_timeout / memory_limit

There is no pure-PHP fallback. If v8js is absent, install via PECL (pecl install v8js), which requires compiling against libv8 — on Laravel Forge/Docker you'll usually need a custom base image, not a runtime apt-get. Pin the V8/PHP versions in your Dockerfile; the combination is brittle.

2. Install and lock the package

composer require reactjs/react-php-v8js

Because the repo is archived, commit composer.lock and treat the version as frozen. No upstream fixes will arrive.

3. Produce a server-renderable JS bundle

The library does not compile JSX. You need a build (Webpack/Browserify/Mix) whose output exposes React and your components as globals, targeting an older JS runtime. React 0.12–15-era builds are the sweet spot; React.renderToString(React.createElement(...)) is what gets called under the hood.

4. First render

use Reactjs\ReactJS;
use Reactjs\ReactComponent;

$react = new ReactJS(
    public_path('js/server.bundle.js'),   // bundle with global React + components
    'HelloWorld'                          // root/global component name
);

$react->setComponent(new ReactComponent('HelloWorld', ['name' => 'Taylor']));

return view('welcome', ['markup' => $react->getMarkup()]);

Blade:

{{-- Mount point must match what the client hydrates --}}
<div id="app">{!! $markup !!}</div>

Where to look first: the ReactJS constructor (bundle path + component name), ReactComponent (name + props payload), and getMarkup(). The README example is the canonical starting point; internal methods like setComponent / getMarkup are the only public surface you should rely on.

First meaningful use case: server-render a data-light component (marketing hero, header/nav shell) into a Blade view, then hydrate it client-side with the same React version. Don't start with authenticated or user-specific content.

Implementation Patterns

Wrap it in a container-bound renderer. Don't call new ReactJS(...) in controllers.

// app/Services/ReactRenderer.php
class ReactRenderer
{
    public function __construct(private string $bundle, private array $components) {}

    public function render(string $component, array $props = []): string
    {
        if (!extension_loaded('v8js') || !isset($this->components[$component])) {
            return ''; // graceful degradation, see Gotchas
        }

        $react = new ReactJS($this->bundle, $component);
        $react->setComponent(new ReactComponent($component, $props));

        return $react->getMarkup();
    }
}

Bind it as a singleton and register component names in config/react.php. This gives you one seam to stub in tests and one place to swap in spatie/laravel-server-side-rendering later.

Use one bundle with a component registry. Rather than one file per component, expose a global map in the server bundle:

// resources/js/server.js (webpack: target 'node', libraryTarget 'var')
window.Components = { HelloWorld, App, Footer };

Then dispatch on component name from PHP. Reduces bundle-path config sprawl and keeps the registry in one place.

Match SSR and client exactly. The props passed to ReactComponent must match what the client uses to hydrate. Build the props array once in a view composer / Inertia-style shared prop, pass it to both PHP and @json() in Blade. Same React version in both builds, or hydration checksums won't align and React will discard the server markup (or worse, silently misbehave).

Blade directive for ergonomics.

Blade::directive('react', function ($expr) {
    return "<?php echo app(\App\Services\ReactRenderer::class)->render({$expr}); ?>";
});
@react("'HelloWorld', ['name' => 'Taylor']")

Cache aggressively. V8 boot plus bundle parse is the dominant cost. Since the render is deterministic per (component, props), cache the markup:

public function renderCached(string $component, array $props = []): string
{
    $key = 'react:' . $component . ':' . md5(json_encode($props));
    return Cache::remember($key, now()->addHour(), fn () => $this->render($component, $props));
}

Restrict to props that don't contain user-specific or time-sensitive fields.

Integrate with Inertia/Mix builds. Add a second Mix/Webpack config (webpack.server.js) producing public/js/server.bundle.js with target: 'node'. Run it in CI, not on request. Commit or artifact the output — production servers should not build JS.

Testing. Bind a fake ReactRenderer in TestCase that returns the props serialized as JSON, and assert markup shape. Keep one integration test that exercises the real bundle so you notice v8js/React version drift.

Gotchas and Tips

The package is archived. Assume no maintenance, no security patches, and no React-16+ react-dom/server support. Treat this as a legacy subsystem: isolate it behind an interface, document the exit path (Node SSR via spatie/laravel-server-side-rendering, or Inertia SSR), and avoid spreading ReactJS calls across the codebase.

v8js installation is the #1 failure mode. The PECL extension requires libv8-dev, has tight PHP-version coupling, and frequently fails to compile in CI or on managed Laravel hosting. Detect at boot (extension_loaded('v8js')) and fall back to an empty string or a loading skeleton — never let a missing extension 500 the page.

No browser globals on the server. window, document, localStorage, and libraries that touch them at module load will throw during render. Guard with typeof window !== 'undefined', or lazy-import browser-only code. This is the most common runtime error with this package.

Props must be JSON-serializable. Closures, resources, Carbon instances, and Eloquent models will be silently dropped or throw. Run props through a transformer (->toArray(), ISO-8601 date strings) before passing. There is no reverse channel — the JS side cannot call back into PHP.

Errors surface as opaque V8 exceptions. Wrap getMarkup() in try/catch, log the component name and props hash, and include the raw message. In development, add a debug path that executes the bundle with a try { ... } catch (e) { return e.stack; } shim so you see the JS stack, not just "V8 threw an exception."

Instance lifecycle and memory. ReactJS objects hold a live V8 context; reusing one across requests in Octane/FrankenPHP can leak memory or carry stale globals. Construct per render, or explicitly rebuild the context, and avoid long-lived singletons of ReactJS itself (singleton the renderer service, not the engine instance).

Timeout and memory limits. Set v8js.script_timeout and v8js.memory_limit in php.ini. A runaway bundle (accidental while(true), huge data dependency) will otherwise tie up php-fpm workers with no PHP-level signal.

Hydration checksums depend on React version parity. The data-react-checksum embedded by SSR must match what the client build computes. Any mismatch — React version, prop ordering, or non-deterministic values (dates, Math.random()) — causes React to throw away the server HTML. Freeze date/time and random values in props.

Output escaping. getMarkup() returns trusted HTML; use {!! !!}. Do not pass it through e() or you'll render escaped markup. Correspondingly, never feed user-supplied HTML into a component's dangerouslySetInnerHTML on the server without sanitizing.

Don't SSR per-user or auth-dependent content. It defeats HTTP caching, complicates hydration, and exposes data in the initial payload. Use SSR for shells and static routes; let the client fetch personalized data after hydration.

Extension point worth knowing: V8JS::setModuleLoader() / registerExtension() (if you drop below this package's wrapper) let you preload modules and polyfill globals inside the V8 context — useful for injecting window shims or shared utilities when third-party libraries assume a browser.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core