Back to Posts

Rails Frontend in 2025: The Complete Landscape

December 05, 2025

·

By GGG

Rails Frontend in 2025: The Complete Landscape

The Rails frontend story has transformed dramatically over the past few years. Gone are the days when your only options were jQuery sprinkles or a full React rebuild. In 2025, Rails offers a sophisticated toolkit that lets you pick the right tool for each job—from server-rendered HTML to full SPAs—all while keeping the Rails conventions you know.

Here’s everything you need to know about building modern frontends with Rails.

The Default Stack: Hotwire

Hotwire (HTML Over The Wire) is the default frontend framework for new Rails applications. It consists of three main components:

Turbo Drive intercepts link clicks and form submissions, making them AJAX requests that replace the page body without a full reload. You get SPA-like speed without writing JavaScript.

Turbo Frames let you define regions of the page that can be updated independently. Wrap a section in a <turbo-frame> tag, and clicks within that frame only update that frame.

Turbo Streams handle real-time updates via WebSockets or server responses, letting you append, prepend, replace, or remove DOM elements from anywhere.

Stimulus is a modest JavaScript framework for adding behaviour to HTML. Rather than taking over your entire frontend, it attaches controllers to existing elements via data attributes.

Rails 8 ships with Turbo 8, which introduces morphing—smarter DOM diffing that smooths out page refreshes with less flicker and fewer Stimulus controller disconnects. Combined with the new Hotwire Native for iOS and Android, you can wrap your Rails app in native shells while keeping server-rendered HTML.

The pitch is compelling: build SPA-like experiences without maintaining a separate frontend application, API layer, or learning React’s state management patterns.

When Hotwire Works Brilliantly

Hotwire excels at:

  • CRUD interfaces and admin panels
  • Forms with inline validation feedback
  • Real-time features (chat, notifications, live updates)
  • Progressive enhancement of server-rendered pages
  • Apps where most interactivity is request-response based

The 37signals team runs Basecamp and HEY entirely on Hotwire. Many Rails developers report building features in hours that would take days with a React SPA approach—particularly when the alternative involves building both an API and a client-side data layer.

A developer on Hey.com described building a drag-and-drop app in under 500 lines that took a friend’s team 2,000+ lines in React just for the frontend.

When Hotwire Falls Short

Hotwire isn’t universally applicable. Teams have reported friction when:

  • Building highly interactive interfaces with complex client-side state
  • Implementing features that require optimistic updates and offline support
  • Working with third-party JavaScript libraries designed for React/Vue
  • Building applications that need to share components with React Native mobile apps

One team’s candid retrospective after 18 months with Hotwire on a 50,000+ DAU SaaS highlights the challenges: complex UI state management becomes awkward, and some interactions that are trivial in React require creative workarounds with Turbo Frames.

The takeaway isn’t that Hotwire is bad—it’s that you need to understand what you’re building before committing to any approach.

JavaScript Bundling Options

Rails offers multiple approaches for handling JavaScript:

Import Maps (Default)

Import maps are the default for new Rails 7+ apps. They let browsers load ES modules directly from URLs without a build step.

Pros:

  • Zero build step, no Node.js required
  • Instant feedback during development
  • Simpler deployment pipeline

Cons:

  • No tree-shaking or dead code elimination
  • Some npm packages don’t work without transpilation
  • No TypeScript support without additional setup
  • Your JavaScript source is visible in production

Import maps work well for apps with minimal JavaScript—Stimulus controllers and Turbo, essentially. If you’re staying within Rails’ defaults, they’re excellent.

esbuild (via jsbundling-rails)

esbuild is blazingly fast (written in Go) and handles most bundling needs. It’s the recommended choice when you outgrow import maps.

rails new myapp -j esbuild

Pros:

  • Extremely fast builds
  • TypeScript support
  • npm package compatibility
  • Tree-shaking and minification

Cons:

  • Requires Node.js in your deployment pipeline
  • Some advanced features (code splitting, CSS handling) are limited

For most Rails apps that need more than import maps, esbuild hits the sweet spot of speed and capability.

Vite (via vite_ruby)

Vite uses esbuild for development and Rollup for production builds, giving you the best of both worlds.

Pros:

  • Lightning-fast HMR (Hot Module Replacement)
  • Mature production optimisation via Rollup
  • React/Vue plugin ecosystem
  • Better CSS handling than esbuild alone

Cons:

  • Additional complexity over esbuild
  • Heavier setup

Vite makes sense if you’re building a more JavaScript-heavy frontend or using Inertia.js with React/Vue.

Bun

Bun is the newest contender—an all-in-one JavaScript runtime that includes a bundler, transpiler, and package manager. Some benchmarks show it 3x faster than esbuild.

It’s still maturing for Rails integration, but worth watching if you want bleeding-edge performance.

CSS: Tailwind is King

Tailwind CSS has become the dominant styling approach in the Rails ecosystem. Rails makes setup trivial:

rails new myapp --css tailwind

The tailwindcss-rails gem includes a standalone Tailwind CLI—no Node.js required. Tailwind v4 simplified configuration further by moving everything into your main CSS file.

Why Tailwind dominates:

  • Utility classes eliminate context-switching between HTML and CSS files
  • PurgeCSS removes unused styles automatically
  • The constraint-based design system promotes consistency
  • Copy-paste component libraries like Flowbite and daisyUI accelerate development

If you prefer traditional CSS or another framework, Rails supports Bootstrap, Bulma, and others via cssbundling-rails. But Tailwind’s momentum in the Rails community is undeniable.

Component Libraries: ViewComponent vs Phlex

As Rails applications grow, ERB partials become unwieldy. Two libraries offer better abstractions:

ViewComponent

ViewComponent, created by GitHub, brings component-based architecture to Rails views. Components are Ruby classes with associated ERB templates.

# app/components/message_component.rb
class MessageComponent < ViewComponent::Base
  def initialize(name:)
    @name = name
  end
end
<%# app/components/message_component.html.erb %>
<h1>Hello, <%= @name %>!</h1>

Pros:

  • Familiar ERB syntax
  • Explicit interfaces (no more implicit instance variables)
  • Unit testable (100x faster than controller tests)
  • Sidecar files keep related assets together

Cons:

  • Multiple files per component
  • Now in long-term support (feature-complete, no major new features)

Phlex

Phlex takes a different approach: write HTML in pure Ruby.

class Nav < Phlex::HTML
  def template
    nav(class: "main-nav") do
      ul do
        li { a(href: "/") { "Home" } }
        li { a(href: "/about") { "About" } }
      end
    end
  end
end

Pros:

  • Single file per component
  • Full Ruby refactoring tools work on views
  • Built-in XSS protection
  • Fragment caching and streaming support

Cons:

  • Learning curve if you’re used to templates
  • Smaller ecosystem than ViewComponent
  • Copy-pasting HTML examples requires conversion

Both work well with Tailwind, Stimulus, and Turbo. ViewComponent feels more familiar; Phlex feels more powerful once you’re comfortable. RubyUI provides a Phlex-based component library if you want pre-built components.

The SPA Alternative: Inertia.js

Inertia.js offers a middle path between Hotwire and a full SPA. It lets you use React, Vue, or Svelte components as your view layer while keeping Rails conventions for routing, controllers, and data fetching.

# app/controllers/posts_controller.rb
def index
  render inertia: 'Posts/Index', props: {
    posts: Post.all.as_json
  }
end

Your React component receives posts as a prop. No API to build, no client-side routing to configure. Inertia handles the protocol.

When to choose Inertia:

  • Your team has React/Vue expertise
  • You need complex client-side interactivity
  • You want to share components with a React Native app
  • Specific npm packages only work with React/Vue

When to stick with Hotwire:

  • Your team is primarily Ruby developers
  • Most pages are standard CRUD
  • You want the simplest possible stack

The inertia_rails gem has matured significantly, with generators and good Vite integration. Evil Martians’ guides are excellent for getting started.

The Hybrid Approach

The emerging consensus is that Rails frontends don’t require a single choice. The Evil Martians’ “silver toolbox” approach:

  1. Hotwire for standard CRUD interfaces, admin panels, and pages where server-side state is king
  2. Turbo Mount for embedding React/Vue components into Hotwire pages when you need isolated interactivity
  3. Inertia.js for full SPAs when you need the full power of a frontend framework

Turbo Mount is particularly clever—it lets you mount React, Vue, or Svelte components directly into Turbo-powered pages without converting your entire app to Inertia.

The Developer Experience in 2025

Rails 8’s frontend story is remarkably complete:

  • Hot reloading: hotwire-livereload gem watches CSS and views, reloading automatically
  • Type checking: TypeScript works with esbuild or Vite
  • Testing: ViewComponent and Phlex components are unit-testable
  • IDE support: RubyLSP, Tailwind IntelliSense, and Stimulus LSP improve the editing experience

The tooling has caught up. You’re no longer making significant sacrifices by staying in the Rails ecosystem for frontend work.

Decision Framework

Choose Hotwire if:

  • Your team is primarily backend developers
  • Most pages are form-based CRUD
  • You want the simplest possible setup
  • Real-time features matter more than complex client state

Choose Inertia.js if:

  • Your team has React/Vue experience
  • You need complex client-side interactivity
  • You want to share components across web and mobile
  • Specific JavaScript libraries require a framework

Choose a hybrid approach if:

  • Different parts of your app have different needs
  • You want to start with Hotwire and add React where needed
  • You’re building a large application with varied requirements

My Take

For most Rails applications in 2025, I’d start with the defaults: Hotwire, Tailwind, and import maps. You can build surprisingly sophisticated UIs without touching React.

When you hit limitations—a complex dashboard, a collaborative editing feature, something that genuinely needs client-side state—reach for Turbo Mount to embed React components, or consider Inertia.js if you’re building multiple such features.

The key insight is that Rails frontend isn’t one size fits all anymore. The ecosystem has matured to offer real choices. Pick the tool that matches your team’s skills and your application’s actual needs, not what’s trending on Twitter.


Published December 2025. The Rails frontend ecosystem continues to evolve rapidly—check gem and library documentation for the latest features.

Further Reading & Resources

Hotwire

JavaScript Bundling

CSS

Components

Inertia.js

Comparisons & Philosophy