Moving from Hotwire to Vite and React

I recently migrated one of my personal Rails projects from Hotwire to Vite and React (via Inertia.js).

Hotwire is great for pure Rails applications, but for this particular project, UI iteration and AI tooling led me to switch. Here is why I made the change and what the trade-offs look like in practice.

Why I Switched

1. Ready-Made Component Libraries (shadcn/ui)

I’m not strong at writing CSS from scratch. In the Rails ecosystem, building polished, accessible UI components (dialogs, dropdowns, combo-boxes) usually means either writing custom Stimulus controllers and CSS or using gems like ViewComponent / Phlex.

In React, shadcn/ui gives you beautifully designed, accessible components built on Radix UI and Tailwind CSS. You copy what you need into your project, and the styling just works. For solo development where design time is limited, this saves hours of tinkering with CSS.

2. Live AI Previews in Chat

Modern AI assistants (ChatGPT, Claude) excel at generating and updating React components with instant interactive previews.

With Hotwire and ERB, my iteration loop was:

  1. Ask AI to write ERB + Stimulus
  2. Copy into the Rails project
  3. Refresh the browser and test
  4. Repeat

With React and shadcn/ui:

  1. Describe the component in chat
  2. See the rendered component directly inside the AI interface
  3. Iterate and refine in real-time
  4. Copy the final component into the codebase

This tighter feedback loop makes frontend prototyping noticeably faster.

3. Snappy DX with Vite

Using Vite with vite-plugin-ruby provides instant server starts and sub-millisecond Hot Module Replacement (HMR). Changes to React components reflect immediately in the browser without full-page reloads, which feels much snappier during active frontend development.

Why Inertia.js Made the Transition Easy

Instead of building a separate GraphQL or REST API backend, I used Inertia.js Rails.

Inertia acts as a glue layer: you keep your Rails routes and controllers as usual, and return render inertia: "Users/Index", props: { users: @users } instead of rendering ERB templates. This allowed me to migrate pages incrementally without rebuilding the application architecture from scratch.

The Real Trade-offs

The switch wasn’t without downsides:

  • Deployment overhead: Moving away from importmaps means introducing Node.js and a build step into Docker images and CI/CD pipelines.
  • Mental context switching: You have to bridge state management between Ruby backend objects and React frontend components.
  • More moving parts: Between Vite, React, Tailwind, and Inertia, there are simply more dependencies to keep updated.

Summary

Rails + Inertia + React + shadcn/ui solved my biggest personal bottleneck: creating clean, accessible UIs quickly without spending days wrestling with custom CSS. If you’re already great at CSS or happy with vanilla Hotwire, the extra build complexity might not be worth it. But for fast prototyping with rich UI components, the combination works surprisingly well.

Related
AI · Claude Code Building Lexio with Two Claudes: A Workflow That Actually Works

How I use Claude Projects for architecture decisions and Claude Code for implementation—keeping thinking and coding deliberately separate, with Lexio as a real-world example.

Mar 13, 2026
Rails · Basecamp Inside Fizzy's Authentication Architecture: A Three-Tier Approach to Multi-Tenant SaaS

How 37signals' Fizzy structures multi-tenant authentication with an Identity → User → Account model, enabling users to belong to multiple organizations cleanly.

Jan 09, 2026
Rails · Kamal My First Rails Deployment Journey with Kamal

Notes on deploying a Rails app with Kamal for the first time: proxy conflicts with Caddy, Cloudflare forward_headers, and debugging Docker volume mounting errors.

Oct 22, 2025
All posts