I want React server-side rendering to be easy. There aren’t a lot of React server-side rendering tools in the Rails world: React on Rails has the largest presence, but it has many bells and whistles that I’m not ready for. I could sidecar Node.js but I really don’t want another piece of infrastructure I have to manage.
I just want something simple to use and easy to get started with. So we built it. Today we’re announcing Humid 1.0!
Humid is just a few helpers for React
server-side rendering in Rails with mini_racer, a minimal and modern embedded
V8 for Ruby.
Install it
gem "humid"
Write your renderer and wrap it with the setHumidRenderer global.
//app/assets/javascript/server_rendering.jsx
import React from 'react'
import { renderToString } from 'react-dom/server'
const Greeting = ({ name }) => <h1>Hello {name}</h1>
setHumidRenderer((json) => {
const props = JSON.parse(json)
return renderToString(<Greeting {...props} />)
})
And render it from Ruby:
require 'humid'
require 'mini_racer'
context = MiniRacer::Context.new
server_bundle = Rails.root.join("app/assets/builds/server_rendering.js")
props = JSON.generate({ name: "World" })
Humid.prepare(context, {application_path: server_bundle})
Humid.render(context, props)
# => <h1>Hello World</h1>
Best of all:
- There’s no separate Node.js process.
- It’s powered by the performance of mini_racer.
- There’s nothing that Humid does that you can’t do by yourself: it’s less than 200 lines of code.
- Instrumentation and Telemetry included.
- You can set
Humid.prepare‘s default options with an initializer.
Design
Humid is designed with two goals in mind:
- It’s for the common case where all data is gathered before rendering. Your application fetches everything needed, passes it as props, and Humid returns the rendered HTML in a single synchronous call.
- It’s a stepping stone for when you want to scale on the edge using
Cloudflare V8 isolates.
mini_racer is a bare V8 environment, if
your JS bundle works with
mini_racer, it’ll work on Cloudflare V8 isolates.
Caveats
Humid renders synchronously. It does not support streaming or async data fetching during render. Gather your data first, then pass it as props.
mini_racer is a bare V8 environment: no window, no document, no Node builtins.
Your build will need polyfills or shims. Here’s a working one for React.
mini_racer is thread safe, but not fork-safe. See guidance here for your webserver.