DEV Community

Alex Aslam
Alex Aslam

Posted on

htmx vs. Hotwire: The Real-Time Showdown

"We rebuilt our Rails app’s frontend twice—once with Hotwire, once with htmx. The results shocked us."

The frontend world is splitting into two camps:

  • Hotwire (Turbo + Stimulus) for Rails-like magic
  • htmx for HTML-centric simplicity

Both promise SPA-like interactivity without JavaScript frameworks, but their philosophies lead to wildly different tradeoffs.

Here’s what happened when we stress-tested them in production.


1. The Core Philosophies

Hotwire (The Rails Way)

"Upgrade HTML with minimal JavaScript."
Turbo Streams: Real-time DOM updates over WebSockets
Stimulus: Sprinkle JavaScript behavior
Deep Rails integration

htmx (The HTML-First Way)

"Why write JS when HTML can do it?"
AJAX in HTML attributes: No .js files for basic interactivity
SSE/WebSocket support: But less turnkey than Turbo
Framework-agnostic


2. The Benchmark Battleground

We tested 3 critical scenarios on a SaaS dashboard:

Test 1: Real-Time Updates

Metric Hotwire (Turbo Streams) htmx (SSE)
Setup Time 5 minutes (rails g channel) 15 minutes (manual SSE endpoint)
Latency (P95) 220ms 190ms
Connection Stability 92% success at 10K users 98% success

Verdict:

  • Hotwire wins for Rails integration
  • htmx wins for raw performance

Test 2: Complex Interactivity

A dynamic form with conditional fields

Hotwire Approach:

<%= form_with model: @invoice, data: { controller: "form" } do |f| %>
  <%= f.select :type, ["Hourly", "Fixed"], data: { action: "form#update" } %>
  <%= turbo_frame_tag "rate_fields" do %>
    <%= render partial: "rate_fields", locals: { f: f } %>
  <% end %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

htmx Approach:

<form hx-post="/invoices" hx-target="#form-container">
  <select name="type" hx-get="/invoice/fields" hx-target="#rate-fields">
    <option value="hourly">Hourly</option>
    <option value="fixed">Fixed</option>
  </select>
  <div id="rate-fields"><!-- Server renders partial --></div>
</form>
Enter fullscreen mode Exit fullscreen mode

Verdict:

  • Hotwire requires JavaScript (Stimulus)
  • htmx achieves the same with zero JS

Test 3: Error Handling

Metric Hotwire htmx
Failed request feedback Silent (requires JS hooks) Automatic (HTML swap)
Retry logic Manual Built-in (hx-retry)

Verdict: htmx handles edge cases more elegantly.


3. Key Tradeoffs

Hotwire Strengths

Batteries-included real-time (Turbo Streams)
Stimulus for gradual JS complexity
Rails generators save hours

htmx Strengths

No JavaScript for basic features
Smaller bundle size (14kb vs. Turbo+Stimulus 45kb)
Works with any backend

When to Choose Hotwire

  • You’re all-in on Rails
  • Your team knows Stimulus
  • You need Turbo Drive for SPA-like nav

When to Choose htmx

  • You hate writing JavaScript
  • Your app is mostly server-rendered
  • You want lighter real-time than WebSockets

4. The Hybrid Approach

We ultimately landed on:

  • htmx for CRUD forms and simple interactivity
  • Hotwire for real-time features (Turbo Streams)
  • Stimulus for complex UI (drag/drop, charts)
<!-- Example hybrid component -->
<div data-controller="chart"
     hx-get="/stats"
     hx-trigger="load">
  <!-- htmx loads data, Stimulus renders chart -->
</div>
Enter fullscreen mode Exit fullscreen mode

5. Migration Tips

From Hotwire to htmx

  1. Replace turbo_frame_tag with hx-target
  2. Swap Stimulus actions for hx- attributes
  3. Keep Turbo Drive for page transitions

From htmx to Hotwire

  1. Add data-turbo="false" to htmx forms
  2. Create channels for real-time updates
  3. Use Stimulus for JS-heavy elements

"But Our App Uses React!"

That’s okay. Start small:

  1. Try htmx for one form
  2. Add Turbo Frames for one real-time feature
  3. Compare team productivity

Which camp are you in? Team Hotwire or Team htmx? Debate below!

Top comments (0)