Humid 1.0: React server-side rendering in Rails can be easy!

https://thoughtbot.com/blog/humid-1-0-react-server-side-rendering-in-rails-can-be-easy
Johny Ho

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

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:

  1. There’s no separate Node.js process.
  2. It’s powered by the performance of mini_racer.
  3. There’s nothing that Humid does that you can’t do by yourself: it’s less than 200 lines of code.
  4. Instrumentation and Telemetry included.
  5. You can set Humid.prepare‘s default options with an initializer.

Design

Humid is designed with two goals in mind:

  1. 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.
  2. 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.

About thoughtbot

We've been helping engineering teams deliver exceptional products for over 20 years. Our designers, developers, and product managers work closely with teams to solve your toughest software challenges through collaborative design and development. Learn more about us.