ToolWall is the world’s first digital organizer for your physical tools. If you like this article or just want to support a small business, please share it with others.

Chapter 1:

Is Rails'

Hotwire

Worth

Using?

For the past three weeks I have been using Rails’ Hotwire framework. Hotwire is Rails’ default way of bringing interactivity to the webpage. The promise is you can build a snappy single page app using mostly html/css and limited Javascript instead of using frontend framework like React/Vue.

Like rest of Rails, if you stay within the guardrails, Rails abstracts away most of the complexity through convention. At first I was enthused with the idea of having just a monolith app instead of an API and frontend app, but soon my enthusiasm was tempered.

Naming is hard

A big problem I ran into with Hotwire is the confusing naming. Hotwire is umbrella term for three libraries: Stimulus, Turbo and yet to be released Strada. Turbo is not to be confused with Turbolinks which is a deprecated Rails library that does something similar. Turbo consists of Turbo Drive, Turbo Frames and Turbo Streams. etc. Maybe it's just me, but none of the naming is intuitive.

It took me a while to even understand what to use. From what I understand, Turbo is used to update an element or multiple elements in the page without a full page reload. Stimulus is a thin frontend Javascript framework to add JS/CSS interactivity to the page. The two can be used together or separately. It would’ve been a whole lot simpler if they did away with the ’Hotwire’ umbrella term and renamed Turbo to something else to distingush it from the deprecated library.

Hotwire implementation

Here’s a simple Todo app that adds new todos without a page refresh using Rails' Hotwire and Stimulus.js. It's very similar to non Hotwire implementation with some turbo references in it.


class TodosController < ApplicationController
  def index
    @todos = [
      Todo.new(title: "My first TODO goes here"),
      Todo.new(title: "Where does your TODO go?")
    ]
  end

  # POST /create takes the incoming params and creates new todo. If this were
  # a real app, it would save to database and return that todo.
  def create
    @todo = Todo.new(title: params[:todo][:title])

    respond_to do |format|
      # Append the newly created todo to 'todos' turbo frame on the page.
      format.turbo_stream do
        render turbo_stream: turbo_stream.append(
          :todos, partial: 'todos/todo', locals: { todo: @todo }
        )
      end
    end
  end
end

<h2>Todos</h2>
<ul>
  <%= turbo_frame_tag "todos" do %>
    <%= render partial: "todos/todo", collection: @todos %>
  <% end %>
</ul>

<!-- data-controller tells Stimulus to connect the "form" controller to this div. -->
<div data-controller="submit">
  <% @todo = Todo.new %>

  <!-- form_target is accessible in the controller as this.formTarget -->
  <!-- action defines which method in the controller to call based on events -->
  <%= form_with model: @todo, data: { "submit-target": "form", action: "turbo:submit-end->submit#reset" } do |form| %>
    <%= form.text_field :title, data: { "submit-target": "input" }, placeholder: "Enter Todo" %>
    <%= form.button 'Add Todo' %>
  <% end %>
</div>

<li>
  <%= todo.title %>
</li>

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  // Define which tags to reference. input is available as this.inputTarget.
  static targets = ["input", "form"];

  // It took way to long to figure out how to reset and focus the input after
  // submit. Finally figured out there existed 'turbo:submit-end' hook that
  // calls this method.
  reset() {
    this.formTarget.reset();
    this.inputTarget.focus();
  }
}

Click through the tabs on top to see more files.

React implementation

Here’s the same app in React. There are two files: Todo.js which setups the state and rendering of todos, while TodoForm.js is just the form.

If I were to build this for production, I would use Next.js for many reasons, the main one being Server Side Rendering. In the example below the todos are fetched from client side, but Next.js would allow you to fetch it from the server side and send that to the client directly.


import React, { useState, createRef } from "react";
import TodoForm from "./TodoForm";

// API call to fetch todos would happen here.
const defaultTodos = ["My first TODO goes here", "Where does your TODO go?"];

export default function Todo() {
  const [todos, setTodos] = useState(defaultTodos);
  const inputRef = createRef();

  function onSubmitFn(event) {
    event.preventDefault();

    // API call to save the todo would happen here.
    setTodos([...todos, inputRef.current.value]);

    inputRef.current.value = "";
    inputRef.current.focus();
  }

  return (
    <>
      <h2>Todos</h2>
      <ul>
        {todos.map((todo, i) => {
          return (<li key={i}>{todo}</li>);
        })}
      </ul>

      <TodoForm inputRef={inputRef} onSubmitFn={onSubmitFn} />
    </>
  );
}

import React from "react";

export default function TodoForm({ inputRef, onSubmitFn }) {
  return (
    <form onSubmit={onSubmitFn}>
      <input ref={inputRef} placeholder="Enter Todo" />
      <button type="submit" >Add</button>
    </form>
  );
}

Click through the tabs on top to see more files.

Was Hotwire worth it?

It was not for me. It felt like trying to put a square peg into a round hole. For example, it took me way too long to figure out how to reset the form after submit and focus on input box.

The reactive approach that React/Vue use is superior in my opinion to the procedural approach. This might be my ignorance talking, but I can’t imagine building something like ToolWall’s Artboard with just Stimulus.js. For apps where the complexity of maintaining a separate frontend app isn’t worth it...perhaps. But if I want to build the next Stripe or ToolWall, I wouldn’t pick it.

Hotwire is maintained by Basecamp and powers hey.com, so I don't imagine it going away anytime soon. That said, as you can expect for a newish library, the documentation is a work in progress. The best part of using something like React/Vue is someone, somewhere has run the same problem you're encountering and has found a solution for it.

On a not technical note, most web related developer job posts I have seen ask for some kind of frontend experience. I'd say learning new technologies that increases the job propects is better time spent. More importantly after learning Hotwire I didn't experience any mindshifts like I originally did with Rails.

Resources

Of course, I recommend you make your own decision. Here are some resources to help you get started.

Next Steps

I'd love to hear your thoughts. Please email [email protected] If you're looking for organization of your physical space, please consider ToolWall.