docs/oss/building-features/node-renderer/basics.md
Pro Feature — Available with React on Rails Pro. Free or very low cost for startups and small companies. Upgrade or licensing details →
See Installation.
The Node Renderer reuses V8 VM contexts across requests for performance. This means module-level state in your server bundle persists across all SSR requests. Any unbounded caches, _.memoize calls, or growing data structures at module scope will leak memory until the worker restarts.
Essential for production:
NODE_OPTIONS=--max-old-space-size=<MB> to prevent V8 from deferring garbage collectionallWorkersRestartInterval and delayBetweenIndividualWorkerRestartsSee the Memory Leaks guide for common leak patterns and how to fix them.
node-renderer is a standalone Node application to serve React SSR requests from a Rails client. You don't need any Ruby code to setup and launch it. You can configure with the command line or with a launch file.
Generator shortcut: Running
rails generate react_on_rails:install --pro(orrails generate react_on_rails:profor existing apps) automatically createsclient/node-renderer.js, adds the Node Renderer process toProcfile.dev, and installs the required npm packages. See Installation for details. The manual setup below is for apps that need custom configuration.
RENDERER_PORTRENDERER_HOSTRENDERER_LOG_LEVELRENDERER_BUNDLE_PATHRENDERER_WORKERS_COUNTRENDERER_PASSWORDRENDERER_ALL_WORKERS_RESTART_INTERVALRENDERER_DELAY_BETWEEN_INDIVIDUAL_WORKER_RESTARTSRENDERER_SUPPORT_MODULES-p <PORT>. For example, assuming node-renderer is in your path:
RENDERER_BUNDLE_PATH=/app/.node-renderer-bundles node-renderer
-p SOME_PORT to override any ENV value for the PORT.For the most control over the setup, create a JavaScript file to start the NodeRenderer.
Create some project directory, let's say renderer-app:
mkdir renderer-app
cd renderer-app
Make sure you have Node.js 18+ and a JavaScript package manager such as npm, pnpm, Yarn, or bun.
Initialize a Node application and install the react-on-rails-pro-node-renderer package.
npm init -y
npm install react-on-rails-pro-node-renderer
# or: pnpm add react-on-rails-pro-node-renderer
# or: yarn add react-on-rails-pro-node-renderer
# or: bun add react-on-rails-pro-node-renderer
Configure a JavaScript file that will launch the rendering server per the docs in Node Renderer JavaScript Configuration. For example, create a file node-renderer.js. Here is a simple example that uses all the defaults except for serverBundleCachePath:
import path from 'path';
import reactOnRailsProNodeRenderer from 'react-on-rails-pro-node-renderer';
const config = {
serverBundleCachePath: path.resolve(__dirname, '../.node-renderer-bundles'),
};
reactOnRailsProNodeRenderer(config);
Now you can launch your renderer server with node node-renderer.js. You will probably add a script to your package.json.
You can use a command line argument of -p SOME_PORT to override any configured or ENV value for the port.
Create config/initializers/react_on_rails_pro.rb and configure the renderer server. See configuration values in Configuration. Pay attention to:
config.server_renderer = "NodeRenderer"config.prerender_caching = true. The default is false; turn it on only if you want Rails cache-backed SSR result caching and your cache is configured for the additional load.renderer_renderer_url so that your deployed server is properly configured. If the ENV value is unset, the default for the renderer_url is localhost:3800.ReactOnRailsPro.configure do |config|
config.server_renderer = "NodeRenderer"
# when this ENV value is not defined, the local server at localhost:3800 is used
config.renderer_url = ENV["REACT_RENDERER_URL"]
end
The Node Renderer executes JavaScript sent to it by the Rails application using Node.js vm.runInContext(). This makes it, by design, a remote code execution service — any client that can reach the HTTP port can execute arbitrary JavaScript on the host machine.
Node.js vm contexts are not a security boundary. Escaping a vm sandbox to access the full Node.js runtime (file system, child processes, network) is well-documented and straightforward.
To mitigate this, the renderer uses the same approach as PostgreSQL: it binds to localhost by default, so it is not reachable from the network at all. This provides two layers of defense:
password setting (required in production-like environments) protects against unauthorized local callers.This means a developer running the renderer locally without a password is safe by default — the renderer is only reachable from their own machine, just as a default PostgreSQL installation only accepts local connections.
When you must bind to 0.0.0.0 (e.g., separate container workloads in Docker Compose or Kubernetes separate-workload deployments):
RENDERER_PASSWORD to a strong valueSee JS Configuration for the host and password options, and Container Deployment for architecture-specific guidance.
The NodeRenderer has a protocol version on both the Rails and Node sides. If the Rails server sends a protocol version that does not match the Node side, an error is returned. Ideally, you want to keep both the Rails and Node sides at the same version.