When I started learning web development, everyone was talking about React, Vue, and Angular. "You need to learn a JavaScript framework," they said. "Single Page Applications are the future."
But then I discovered Hotwire, and it changed everything. In this post, I'll explain why I chose Hotwire over React as a beginner, and why it might be the right choice for you too.
The JavaScript Framework Overwhelm
Before diving into Hotwire, let me paint a picture of what learning React felt like as a beginner:
The React Learning Curve
// Just to display a list of tasks, I needed to understand:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function TaskList() {
const [tasks, setTasks] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchTasks();
}, []);
const fetchTasks = async () => {
try {
const response = await axios.get('/api/tasks');
setTasks(response.data);
setLoading(false);
} catch (error) {
console.error('Error fetching tasks:', error);
setLoading(false);
}
};
if (loading) return <div>Loading...</div>;
return (
<div>
{tasks.map(task => (
<div key={task.id}>{task.title}</div>
))}
</div>
);
}
What I had to learn for this simple feature:
- JSX syntax
- useState and useEffect hooks
- Async/await and promises
- API calls with axios
- State management
- Component lifecycle
- Error handling
- Build tools (Webpack, Babel)
- Node.js and npm
The Complexity Spiral
As my React apps grew, so did the complexity:
- State management libraries (Redux, Zustand)
- Routing libraries (React Router)
- Form libraries (Formik, React Hook Form)
- HTTP libraries (Axios, Fetch)
- Build configurations
- Testing frameworks
- Type checking (TypeScript)
I was spending more time learning JavaScript tooling than building features.
Discovering Hotwire
Then I found Hotwire, and everything clicked. Here's how the same task list looks with Hotwire:
The Hotwire Approach
Controller (Ruby):
# app/controllers/tasks_controller.rb
class TasksController < ApplicationController
def index
@tasks = current_user.tasks
end
end
View (ERB):
<!-- app/views/tasks/index.html.erb -->
<h1>My Tasks</h1>
<div id="tasks">
<%= render @tasks %>
</div>
Partial:
<!-- app/views/tasks/_task.html.erb -->
<div class="task">
<%= task.title %>
</div>
That's it. No JavaScript, no build tools, no complex state management. The same feature that took dozens of lines of React code works with just a few lines of Rails.
Why Hotwire Made Sense for Me
1. One Language, One Framework
With Hotwire, I could focus on mastering Ruby and Rails instead of context-switching between:
- Ruby for the backend
- JavaScript for the frontend
- Different patterns and conventions
- Separate deployment processes
2. Progressive Enhancement
Hotwire starts with server-rendered HTML that works without JavaScript, then enhances it:
<!-- This works even if JavaScript fails -->
<%= form_with model: @task do |form| %>
<%= form.text_field :title %>
<%= form.submit "Create Task" %>
<% end %>
<!-- Hotwire makes it feel like a SPA -->
<%= form_with model: @task, data: { turbo_frame: "tasks" } do |form| %>
<%= form.text_field :title %>
<%= form.submit "Create Task" %>
<% end %>
3. No Build Step
With React, every change required:
npm run build
# Wait for webpack to bundle everything
# Refresh browser
# Debug source maps
With Hotwire:
```bash
Just refresh the browser
Everything works instantly
### 4. **Familiar Patterns**
Hotwire extends Rails patterns I already knew:
- Controllers handle requests
- Views render HTML
- Models manage data
- Routes define URLs
React required learning entirely new patterns:
- Components and props
- State and effects
- Context and providers
- Reducers and actions
## Real-World Example: Adding Real-Time Features
Let's say I want to add real-time task updates. Here's how each approach looks:
### React Approach
```javascript
// Need WebSocket library
import io from 'socket.io-client';
function TaskList() {
const [tasks, setTasks] = useState([]);
useEffect(() => {
const socket = io('/tasks');
socket.on('task_created', (task) => {
setTasks(prev => [...prev, task]);
});
socket.on('task_updated', (updatedTask) => {
setTasks(prev => prev.map(task =>
task.id === updatedTask.id ? updatedTask : task
));
});
return () => socket.disconnect();
}, []);
// ... rest of component
}
Hotwire Approach
# app/models/task.rb
class Task < ApplicationRecord
after_create_commit -> { broadcast_prepend_to "tasks" }
after_update_commit -> { broadcast_replace_to "tasks" }
end
<!-- app/views/tasks/index.html.erb -->
<%= turbo_stream_from "tasks" %>
<div id="tasks">
<%= render @tasks %>
</div>
The Hotwire version is simpler, requires no JavaScript knowledge, and leverages Rails conventions I already understand.
When Hotwire Might Not Be Right
To be fair, Hotwire isn't perfect for every situation:
React Might Be Better For:
- Highly interactive UIs - Complex dashboards, games, drawing apps
- Offline-first apps - Apps that need to work without internet
- Mobile apps - React Native for cross-platform mobile
- Large frontend teams - When you have dedicated frontend developers
- Complex client-side logic - Heavy calculations, data transformations
Hotwire Excels At:
- Traditional web apps - CRUD applications, blogs, e-commerce
- Small teams - Full-stack developers wearing many hats
- Rapid prototyping - Getting ideas to market quickly
- Server-side strengths - Leveraging Rails' ecosystem
- Progressive enhancement - Apps that work without JavaScript
The Learning Path Difference
React Learning Path:
- Learn JavaScript (ES6+)
- Learn React basics (components, JSX)
- Learn React hooks (useState, useEffect)
- Learn state management
- Learn routing
- Learn build tools
- Learn testing
- Learn backend integration
- Start building features
Hotwire Learning Path:
- Learn Rails basics
- Add
data-turbo-frame
to forms - Add
turbo_stream_from
for real-time - Start building features
With Hotwire, I was building real features by day 3. With React, I was still setting up my development environment.
Performance Considerations
One concern people raise about Hotwire is server load. Here's what I've found:
React Performance:
- Initial load: Large JavaScript bundles
- Runtime: Fast client-side interactions
- Server: Minimal load (API calls only)
- Caching: Complex client-side caching
Hotwire Performance:
- Initial load: Fast server-rendered HTML
- Runtime: Small network requests for updates
- Server: Higher load (but cacheable)
- Caching: Simple server-side caching
For most applications, server-side rendering is actually faster and more reliable than client-side rendering.
Developer Experience
React Development:
# Start frontend server
npm start
# Start backend server (separate terminal)
rails server
# Run tests (another terminal)
npm test
# Build for production
npm run build
Hotwire Development:
# Start server
rails server
# That's it
The simplicity is liberating. I spend time building features, not managing toolchains.
My Recommendation
Choose Hotwire if you:
- Are learning web development
- Want to focus on one language/framework
- Are building traditional web applications
- Value simplicity over complexity
- Want to ship features quickly
- Are working on a small team
Choose React if you:
- Are building highly interactive UIs
- Have dedicated frontend developers
- Need offline functionality
- Are building mobile apps
- Have complex client-side requirements
The Hybrid Approach
You don't have to choose exclusively. Many successful apps use:
- Hotwire for 90% of the application
- React for specific interactive components
- Stimulus for small JavaScript enhancements
This gives you the best of both worlds.
What I've Built with Hotwire
Since choosing Hotwire, I've built:
- A task management app with real-time updates
- A blog with instant comment posting
- An e-commerce site with dynamic cart updates
- A chat application with live messaging
All without writing complex JavaScript or managing build tools.
The Mental Model Shift
The biggest difference isn't technical—it's mental:
React mindset: "How do I manage state and sync it with the server?"
Hotwire mindset: "How do I make server-rendered HTML feel interactive?"
For me, the Hotwire mindset aligned better with how I think about web applications.
Conclusion
Choosing Hotwire over React as a beginner was one of my best decisions. It allowed me to:
- Focus on learning one framework deeply
- Ship features faster
- Avoid JavaScript framework fatigue
- Build on Rails' mature ecosystem
- Keep my applications simple and maintainable
This doesn't mean React is bad—it's an excellent tool for certain use cases. But for beginners building traditional web applications, Hotwire offers a more approachable path to modern, interactive web apps.
Key takeaways:
- Hotwire extends Rails patterns you already know
- No build tools or complex JavaScript required
- Progressive enhancement keeps apps accessible
- Real-time features are simpler to implement
- Focus on building features, not managing tools
In my next post, I'll show you how to build your first Hotwire feature with Turbo Frames, turning a traditional form submission into an instant, no-refresh experience.
Next up: "Turbo Frames: My First 'Wow' Moment with Hotwire" - We'll build an interactive feature that updates without page refreshes.