JavaScript view layer for BEAR.Sunday
This module enables you to write views in JavaScript while keeping your application logic in PHP. The JavaScript templates are executed server-side (SSR) for initial rendering and can hydrate on the client for interactivity.
- You want to write views in JavaScript (React, Vue, etc.) within a PHP application
- You prefer to keep server-side application logic in BEAR.Sunday while using JavaScript for UI
- You need both server-side rendering and client-side hydration with the same view code
- PHP 8.2+
- Node.js (for SSR execution)
- V8Js (Optional - for embedded execution without process overhead)
composer require bear/ssr-module$buildDir = dirname(__DIR__, 2) . '/var/www/build';
$this->install(new SsrModule($buildDir));Place your {app}.bundle.js file in the $buildDir directory. This JS is used for server side rendering (SSR) only.
use BEAR\SsrModule\Annotation\Ssr;
#[Ssr(app: 'index_ssr')]
public function onGet(): static
{
$this->body = [
'name' => 'World',
];
return $this;
}Add the #[Ssr] attribute to methods where you want SSR. Set the JS application name with app.
Here is a minimalistic JS application. Export a render function.
Use koriym/js-ui-skeleton to create a JavaScript UI application.
const render = state => (
`Hello ${state.name}`
)In SSR applications, you may need two kinds of data:
state: Public data sent to the client (included in HTML)metas: Server-side only data
Separate them using the state and metas parameters in the #[Ssr] attribute:
use BEAR\SsrModule\Annotation\Ssr;
#[Ssr(app: 'index_ssr', state: ['name', 'age'], metas: ['title'])]
public function onGet(): static
{
$this->body = [
'name' => 'World',
'age' => 4.6E8,
'title' => 'Age of the World',
];
return $this;
}render.js:
const render = (preloadedState, metas) => {
return `<html>
<head>
<title>${escape(metas.title)}</title>
</head>
<body>
<script>window.__PRELOADED_STATE__ = ${serialize(preloadedState)}</script>
</body>
</html>`;
};
export default render;For production, use cache modules to improve performance:
$this->install(new ApcSsrModule($buildDir));use BEAR\SsrModule\Annotation\SsrCacheConfig;
use Psr\SimpleCache\CacheInterface;
// Bind your PSR-16 cache implementation
$this->bind(CacheInterface::class)
->annotatedWith(SsrCacheConfig::class)
->to(YourCacheImplementation::class);
$this->install(new CacheSsrModule());
$this->install(new SsrModule($buildDir));This module uses koriym/baracoa for JavaScript execution, which supports two runtimes:
| Runtime | Pros | Cons |
|---|---|---|
| Node.js (default) | No PHP extension required, easy deployment | Process spawn overhead per render |
| V8Js | Embedded execution, no process overhead | Requires PHP extension installation |
Node.js is used automatically when V8Js is not available.
When using event-driven caching with TTL=0 (cache invalidated by events rather than time), the Node.js process overhead becomes negligible. The rendered HTML is cached indefinitely and invalidated only when the underlying data changes, so JavaScript execution occurs only on cache misses.
