laravel-server-side-rendering runs a Node or V8 script inside a Blade view
Server side rendering JavaScript in your Laravel application
At a glance
- What is it?
- This package bridges spatie/server-side-rendering into Laravel, so a Blade view can call an ssr helper that executes a JavaScript bundle and returns rendered HTML. It is a thin bridge rather than a rendering engine, and it assumes Laravel Mix, a node binary and a writable temp directory are already in place.
- Who is it for?
- Adopt this package if you already run Laravel with Laravel Mix, need a server rendered HTML fragment inside a Blade view, and can commit to shipping a node binary or the v8js extension on the server. Do not adopt it if you want a single JavaScript application shell, since this renders a component and hands the HTML back to Blade rather than hydrating a full frontend.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The ssr helper is a Blade call that shells out to a JavaScript engine
The whole idea fits into one Blade line. A view calls ssr with the path to a server bundle, and the package returns the rendered HTML:
{!! ssr('js/app-server.js') !!}
<script src="{{ mix('js/app-client.js') }}">The first line is the server side render, the second loads the client bundle so the page becomes interactive in the browser. The same structure appears in the fuller example at the top of the README, where the client script is deferred in the head and the ssr call sits in the body.
Two entry points are offered. The helper returns an object you can chain onto, and a facade exists for the same thing, so Ssr::entry('js/app-server.js')->render() produces the same output as the helper form. The README is explicit that the helper is for rendering your app in your view, and that a facade is available too.
The important thing to understand is where the work happens. The package does not run JavaScript in PHP. It hands a script to a separate engine, waits for that engine to call a dispatch function with the HTML, and injects the result into the response. The README states that the underlying library ships with V8 and Node engines, and that the package is a Laravel bridge for spatie/server-side-rendering.
A server bundle and a client bundle, and the dispatch callback that returns the HTML
You need two builds, and the README asks you to consult your frontend framework's documentation for how to produce them. The example uses Laravel Mix:
mix.js('resources/js/app-client.js', 'public/js')
.js('resources/js/app-server.js', 'public/js');Both bundles import the same application entry, and they differ in what they do with it. The server bundle renders the component to a string and sends it back, while the client bundle mounts the same component in the browser. The README's Vue example shows the server side of that:
// resources/js/app-server.js
import app from './app';
import renderVueComponentToString from 'vue-server-renderer/basic';
renderVueComponentToString(app, (err, html) => {
if (err) {
throw new Error(err);
}
dispatch(html);
});The dispatch call is the contract with the PHP side. The server script is responsible for producing HTML and handing it over, and the README states that your server script should call a dispatch function to send the rendered html back to the view. If the render fails, the example throws, and that error surfaces in your Laravel log rather than being swallowed.
The package assumes you are using Laravel Mix and will resolve the path for you, and you can opt out by setting mix to false in the config file.
Context is how state crosses from PHP into the JavaScript bundle
A server rendered component usually needs data. The way that data gets in is the context option, and it chains onto the call:
<html>
<head>
<title>My server side rendered app</title>
<script defer src="{{ mix('js/app-client.js') }}"></script>
</head>
<body>
{!! ssr('js/app-server.js')->context('user', $user)->render() !!}
</body>
</html>That line passes a user object under the key user. On the JavaScript side the matching data is read from the context, which is the part documented in the underlying spatie/server-side-rendering readme under rendering options, and the README of this package points there for the full list of available options.
This is the mechanism that makes the package more than a static renderer. Blade can pass a model, a query result or a config array into the render, the server bundle consumes it, and the returned HTML already reflects server state. The same context value is what a client side bundle would otherwise have to fetch over HTTP, which is the whole argument for doing this work on the server.
The limit worth naming is that context is per call and goes through the engine boundary as data, so it has to be serialisable. The README does not document the constraints on what can be passed, which is one of the things to check against the underlying library before you design around it.
node is the default engine, and it needs a path, a temp folder and a binary
Before any of that works, the runtime has to be set up. The README lists the prerequisites. First you pick an engine to execute your scripts, and the underlying library ships with V8 and Node engines. By default the package is configured to use node, since you probably already have that installed.
For node, the environment variable to set is NODE_PATH, and it goes in your .env file:
NODE_PATH=/path/to/my/nodeSecond, you need to make sure a storage/app/ssr folder exists, or point the ssr.node.temp_path config value somewhere else. The temp directory matters because the engine writes intermediate output there during a render.
If you would rather use V8, the README says you can skip those two steps, but you will need the v8js PHP extension installed. That is the trade-off between the two engines: node needs an external binary and a path, V8 needs a compiled PHP extension. The v8js route keeps everything inside the PHP process, which is simpler to deploy if the extension is already available for your PHP version.
composer require and one optional publish step
Installation is a single Composer command:
composer require spatie/laravel-server-side-renderingThe README notes that the service provider and the Ssr alias are registered automatically, so there is no manual provider entry in config/app.php to add. The alias is what makes the helper available in Blade without importing anything.
Publishing the config file is optional, and it only matters if you want to change the defaults:
php artisan vendor:publish --provider="Spatie\Ssr\SsrServiceProvider" --tag="config"Beyond that, the README states that no configuration is required. The config file at config/ssr.php is described as well documented, and the repository layout confirms it, with a config/ directory alongside src/ and tests/.
Tests for the package itself run through Composer:
composer testThe repository carries a phpunit.xml.dist, a .php-cs-fixer.php and a .github/ directory with a run-tests workflow, which is the badge at the top of the README.
What this bridge does not do for you
The clearest limit is that this renders a piece of HTML into a Blade view, not a whole JavaScript application. The pattern in the README is a Vue or React component rendered on the server, injected into the page, and then mounted in the browser. If your goal is a single page app with client side routing, a virtual DOM and no Blade at all, this package is working against your architecture rather than for it.
The second limit is the deployment surface. Even with the V8 engine, you are running a JavaScript engine inside a PHP request, and with node you are running a second process per render. The README does not discuss caching of rendered output, so a hot endpoint that renders on every request pays that cost every time, and nothing in the package suggests it will cache for you.
The third limit is the dependency on the underlying library's maturity. This package is described as a bridge, and the README tells you to dig through the underlying spatie/server-side-rendering readme to learn about the concepts and caveats, and it also assumes you already have some know-how about building server rendered JavaScript apps. That is an honest statement of where the difficulty lives, and it is the main reason to read both readmes before committing.
The alternative in the same ecosystem is spatie/laravel-server-side-rendering-examples, the Vue and React example apps the README points to. They are not a different implementation, they are the working reference for how the pieces are supposed to fit together, which is what you want when the documentation is thin on the application side.
MIT licensed, moving in small steps, with the last push in June 2026
The licence is MIT, stated in the README and recorded in a LICENSE.md at the repository root, so the code can be used in commercial Laravel applications without copyleft obligations. This is a licensing note rather than legal advice, and the third party JavaScript bundles you render remain under their own terms.
On maintenance, the record is steady but unhurried. The last push was on 2026-06-02 and the repository is not archived. The release history shows 1.6.0 on 2026-02-22, 1.5.2 on 2025-04-10 and 1.5.1 on 2025-02-14.
Those are patch level releases, which tells you the package is being kept compatible rather than extended. A minor bump in a bridge package usually means a Laravel or dependency compatibility fix rather than a new feature, and here you would get new capabilities from the underlying spatie/server-side-rendering library rather than from this one.
The upgrade cost for a Laravel project is therefore the cost of tracking two packages. When the bridge moves, the underlying library moves with it, and the version of spatie/server-side-rendering that your app pulls in decides what the ssr helper and the Ssr facade can do. The CHANGELOG.md in the repository is the place to read what actually changed in a given release.
Editorial conclusion
Adopt this package if you already run Laravel with Laravel Mix, need a server rendered HTML fragment inside a Blade view, and can commit to shipping a node binary or the v8js extension on the server. Do not adopt it if you want a single JavaScript application shell, since this renders a component and hands the HTML back to Blade rather than hydrating a full frontend. Verify first that the engine you choose is available in your deployment environment, that the storage/app/ssr directory exists or that ssr.node.temp_path points somewhere writable, and that your Laravel Mix build emits the separate server and client scripts the ssr helper expects.
Frequently asked questions
How do I install spatie/laravel-server-side-rendering in a Laravel app?
Run composer require spatie/laravel-server-side-rendering. The service provider and the Ssr alias are registered automatically, and you can publish the config file with php artisan vendor:publish using the Spatie\Ssr\SsrServiceProvider provider and the config tag.
How do I render a component in a Blade view with the ssr helper?
Call ssr with the path to your server bundle and chain the options you need, for example {!! ssr('js/app-server.js')->context('user', $user)->render() !!}. A facade form exists too, Ssr::entry('js/app-server.js')->render().
What does the server JavaScript bundle need to do for SSR to work?
It should render the app and call a dispatch function to send the rendered HTML back to the view. The README's Vue example imports renderVueComponentToString and calls dispatch(html) in the callback, throwing if an error is returned.
What does laravel-server-side-rendering need on the server?
By default it uses the node engine, so you need to set the NODE_PATH environment variable in your .env file and make sure a storage/app/ssr folder exists or change ssr.node.temp_path. If you use the V8 engine instead you need the v8js PHP extension installed and can skip those two steps.
Can I use laravel-server-side-rendering without Laravel Mix?
Yes. The package assumes you are using Laravel Mix and will resolve the path for you, but you can opt out by setting mix to false in the config file and then pass the path to your bundle directly.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/spatie-laravel-server-side-rendering)Was this useful?