<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:thoughtbot="https://thoughtbot.com/feeds/">
  <title>Giant Robots Smashing Into Other Giant Robots</title>
  <subtitle>Written by thoughtbot, your expert partner for design and development.
</subtitle>
  <id>https://robots.thoughtbot.com/</id>
  <link href="https://thoughtbot.com/blog"/>
  <link href="https://feed.thoughtbot.com" rel="self"/>
  <updated>2026-08-04T00:00:00+00:00</updated>
  <author>
    <name>thoughtbot</name>
  </author>
<entry>
  <title>Humid 1.0: React server-side rendering in Rails can be easy! </title>
  <link rel="alternate" href="https://thoughtbot.com/blog/humid-1-0-react-server-side-rendering-in-rails-can-be-easy"/>
  <author>
    <name>Johny Ho</name>
  </author>
  <id>https://thoughtbot.com/blog/humid-1-0-react-server-side-rendering-in-rails-can-be-easy</id>
  <published>2026-08-04T00:00:00+00:00</published>
  <updated>2026-08-03T00:55:33Z</updated>
  <content type="html">&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;I just want something simple to use and easy to get started with. So we built
it. Today we’re announcing &lt;a href="https://github.com/thoughtbot/humid"&gt;Humid 1.0&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/thoughtbot/humid/main/images/svg/humid-icon.svg" width="150" alt="Humid"&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/thoughtbot/humid"&gt;Humid&lt;/a&gt; is just a few helpers for React
server-side rendering in Rails with &lt;code&gt;mini_racer&lt;/code&gt;, a minimal and modern embedded
V8 for Ruby.&lt;/p&gt;

&lt;p&gt;Install it&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"humid"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Write your renderer and wrap it with the &lt;code&gt;setHumidRenderer&lt;/code&gt; global.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//app/assets/javascript/server_rendering.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;renderToString&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nf"&gt;setHumidRenderer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;renderToString&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Greeting&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And render it from Ruby:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'humid'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'mini_racer'&lt;/span&gt;

&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;MiniRacer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;server_bundle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"app/assets/builds/server_rendering.js"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"World"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="no"&gt;Humid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;application_path: &lt;/span&gt;&lt;span class="n"&gt;server_bundle&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="no"&gt;Humid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; &amp;lt;h1&amp;gt;Hello World&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Best of all:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There’s no separate Node.js process.&lt;/li&gt;
&lt;li&gt;It’s powered by the performance of &lt;a href="https://github.com/rubyjs/mini_racer/"&gt;mini_racer&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;There’s nothing that Humid does that you can’t do by yourself: it’s less than
200 lines of code.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/thoughtbot/humid/tree/main#call-humidrender"&gt;Instrumentation&lt;/a&gt; and &lt;a href="https://github.com/thoughtbot/humid/tree/main#telemetry"&gt;Telemetry&lt;/a&gt; included.&lt;/li&gt;
&lt;li&gt;You can set &lt;code&gt;Humid.prepare&lt;/code&gt;‘s default options with an &lt;a href="https://github.com/thoughtbot/humid/tree/main#configuration"&gt;initializer&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="design"&gt;
  
    Design
  
&lt;/h2&gt;

&lt;p&gt;Humid is designed with two goals in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;li&gt;It’s a stepping stone for when you want to scale on the edge using
&lt;a href="https://developers.cloudflare.com/workers/reference/how-workers-works/"&gt;Cloudflare V8 isolates&lt;/a&gt;.
&lt;a href="https://github.com/rubyjs/mini_racer"&gt;mini_racer&lt;/a&gt; is a bare V8 environment, if
your JS bundle works with &lt;code&gt;mini_racer&lt;/code&gt;, it’ll work on Cloudflare V8 isolates.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="caveats"&gt;
  
    Caveats
  
&lt;/h2&gt;

&lt;p&gt;Humid renders synchronously. It does not support streaming or async data
fetching during render. Gather your data first, then pass it as props.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mini_racer&lt;/code&gt; is a bare V8 environment: no window, no document, no Node builtins.
Your build will need polyfills or shims. Here’s &lt;a href="https://github.com/thoughtbot/humid/blob/main/sample/shim.js"&gt;a working one&lt;/a&gt; for React.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mini_racer&lt;/code&gt; is thread safe, but not fork-safe. See &lt;a href="https://github.com/thoughtbot/humid/tree/main#your-webserver"&gt;guidance here&lt;/a&gt; for your webserver.&lt;/p&gt;

&lt;aside class="related-articles"&gt;&lt;h2&gt;If you enjoyed this post, you might also like:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/this-week-in-open-source-6-30"&gt;This Week in Open Source (June 30, 2023)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/heroku-wearing-suspenders"&gt;Heroku Wearing Suspenders&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thoughtbot.com/blog/this-week-in-open-source-37"&gt;This Week in Open Source&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/aside&gt;
</content>
  <summary>We're releasing Humid 1.0. A few helper methods to help with React server-side rendering in Rails with mini_racer. </summary>
  <thoughtbot:auto_social_share>true</thoughtbot:auto_social_share>
</entry>
</feed>
