How to handle properly frontend assets in Rails, without being dependent of instabilities of architectural choices. ViteJS is the best option so far IMHO.

· Ruby  · 6 min read

Rails and ViteJS a wonderful combination

How to handle properly frontend assets in Rails, without being dependent of instabilities of architectural choices. ViteJS is the best option so far IMHO.

0. Motivation

Hot Module Reloading (Google around) is a fantastic feature that prevent the develper to nervously press the “refresh” button of the browser, on each code modification

The past frontend assets management of Ruby-on-Rails could feel hacky for many developers :

  • [live/auto/hot] reloading of HTML pages still doesn’t work natively,
  • Serving assets with Rails has changed multiples times, from nothing (Sprockets appeared with Rails 3.1), to not-completely-ready importmaps. Currently Rails support jsbundling, that wraps esbuild (or optionally another tool…), before being injecting again to Sprockets. “importmaps” are optionally supported on top of this. And css bundling works another way…

Recently Rails announced HMR (after all these years!) for Hotwire…

ut, what if you want a proper, unified, elegant, performant assets frontend management right from the start ?

There’s a gem for that

1. Prerequisites

Check that you have ruby 3 already installed. Check you also have bundler installed, and npm above version 7

$> ruby -v
ruby 3.1.0p0 // you need at least version 3 here
$> bundle -v
Bundler version 2.2.10
$> foreman -v
0.87.2
$> npm -v
7.1.0 // you need at least version 7.1 here

Any upper versions should work.

2. Create a new minimalistic Rails application

mkdir railsvite && cd railsvite
echo "source 'https://rubygems.org'" > Gemfile
echo "gem 'rails', '7.0.1'" >> Gemfile
bundle install
bundle exec rails new . --force --css=bootstrap --minimal
bin/rails db:create
bin/rails db:migrate

3. Install vite_rails

Open the Gemfile, and add

gem 'vite_rails'

Then in your terminal

bundle install
bundle exec vite install

4. What vite_rails installed into your Rails app

.gitignore was changed

# Vite Ruby
/public/vite
/public/vite-dev
/public/vite-test
node_modules
*.local
.DS_Store

Ok, not much surprises here. Vite.js is here to handle npm assets, so node_modules is safely ignored. What’s inside public/ is technically required by vite_rails, so far we don’t have to know what it is about.

.Procfile.dev

Since Rails 7, you need foreman to start your local server with ./bin/dev. This command actually calls foreman under the hood, looking for the local .Procfile.dev

vite_rails overrides the default one as follow :

vite: bin/vite dev
web: bin/rails s

Ok, the frontend part runs separately from the server.

app/frontend/entrypoints/application.js

Wow ! Magic starts to happens right now. Instead of a separate directory for javascript, and other kind of assets, everything is now managed from app/frontend. That makes things a lot clearer. The content of this file is mostly docs, and a “hello world” from the console, so we won’t investigate it right now (but you can do it, for curiosity).

app/views/layouts/application.html.erb

The interesting part is here :

<%= vite_client_tag %>
<%= vite_javascript_tag 'application' %>

vite_client_tag : Renders the Vite client to enable Hot Module Reload, according to the docs.

vite_javascript_tag ‘application’ : well, at first sight, this tag should include the javascript generated by vite.

So no more unclear javascript and stylesheet inclusion : now Vite rears everything for us.

package.json

{
  "devDependencies": {
    "vite": "^2.7.3",
    "vite-plugin-ruby": "^3.0.4"
  }
}

vite.config.ts

import { defineConfig } from 'vite';
import RubyPlugin from 'vite-plugin-ruby';

export default defineConfig({
  plugins: [RubyPlugin()],
});

What we can understand from vite.config.js is that we will profite from vite.js itself. So all docs from Vite.js are profitable for our project. See how nicely the RubyPlugin is included. It doesn’t litter our configuration, and left all

Vite itself is included. Nothing hidden then, the vite-ruby-plugin is here to bridge the gap between Vite and the Rails application (tag helpers…)

Other files changed

Less interesting, but worth mentioning : package-lock.json is changed (obviously), config/initializers/content_security_policy.rb is also changed for security reasons, and bin/vite is added to start vite itself when launching a local server.

4. End of the first-half ⚽

That’s it ! Just by having a sneak peek at the code, everything seems elegant and intuitive from the start. Perfect ! 👌

5. Create minimal files

Ok, so let’s test each of the expectations. First, we need the bare minimum files.

# inside app/controllers/home_controller.rb
class HomeController < ApplicationController
end
# inside config/routes.rb
Rails.application.routes.draw do
  get "home/index"
  root to: "home#index"
end
<!-- inside app/views/home/index.html.erb -->
<h1>This is h1 title</h1>

<button>This is a button</button>

These 3 files are enough to trigger a default view. Let’s try.

$/myapp> foreman start -f Procfile.dev

And open your browser

Title and button
Title and button

Great !

6. CSS with Vite and Rails

create file app/frontend/entrypoints/application.css

// inside app/frontend/entrypoints/application.css
h1 {
  text-decoration: underline;
}

change app/views/layouts/application.html.erb

<-- inside app/views/layouts/application.html.erb -->
<html>
  <head>
    <title>Myapp</title>
    <%= vite_client_tag %> <%= vite_javascript_tag 'application' %>
    <!-- add line below, remove old stylesheet_link_tag -->
    <%= vite_stylesheet_tag 'application' %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

Now stop your local server

Restart it :

$/myapp> foreman start -f Procfile.dev

And open your browser at localhost

Underlined
Underlined

We’ve just seen how Vite is able to handle stylesheets : just add an entrypoint in the well-named app/frontend/entrypoints folder, and reference it with the vite_stylesheet_tag helper in your layout.

7. Hot reloading with Rails and Vite

“Hot reloading” means that the browser will automatically refresh itself, each time you change/save anything in your code editor.

For those who already used anything like React/Vue/whateverJSframework, it sounds already pretty obvious, because pressing “f5” into the browser each time is tedious when you have to write dozens of lines of code every day.

But believe it or not, it still doesn’t apply to the default Rails app, even for the recent Rails 7 version.

Let’s try it, when Vite is installed.

Hot reloading CSS

Your localserver should be started. Comment and save the entire app/frontend/entrypoints/application.css file, and open your browser. The style of the title should have disappeared. Now uncomment and save the entire app/frontend/entrypoints/application.css file. The style of title should be underlined in your browser, automagically.

Hot reloading just works ! 🎉🎉🎉

Hot reloading JS

Try the same experience with app/frontend/entrypoints/application.js. You should see changes instantly happen in your browser.

Hot reloading HTML

If you try to change and save app/views/home/index.html.erb. What happened in your browser ? nothing. Uh-oh.

It’s time to see how to add a plugin to ViteJS.

8. Add a plugin to ViteJS, with Rails

Hopefully for our HTML, ViteJs has a plugin dedicated to this task.

$/myapp> yarn add -D vite-plugin-full-reload

Quickly check the plugin was installed in package.json, then open vite.config.ts

// inside vite.config.ts
import { defineConfig } from 'vite';
import RubyPlugin from 'vite-plugin-ruby';
import FullReload from 'vite-plugin-full-reload';

export default defineConfig({
  plugins: [RubyPlugin(), FullReload(['config/routes.rb', 'app/views/**/*'], { delay: 200 })],
});

Relaunch your local server. Try to change and save app/views/home/index.html.erb, now any change to the view automagically appears in your browser !

9. Credits

10. What a gem !

It’s already a long tutorial, so that’s it for today, but there will be more articles about the powerful Vite.

We have seen how ViteJS completely replaces the default Rails frontend assets management, in an intuitive manner.

Then we’ve seen how hot reloading works, and how to add a Vite plugin to our application to achieve a nice development workflow.

Enjoy !

Share:
Back to Blog

Related Posts

View All Posts »
Rails and Sidekiq tutorial

Rails and Sidekiq tutorial

The need for the existence of background jobs arises very quickly when you create a new Rails application for business purposes. Sidekiq was, for a long time, the de-facto tool of the Rails stack.

Rails and Cypress testing

Rails and Cypress testing

Cypress is very interesting, since it matches one of the core Rails philosophy, which is "value integrated system"

The simplest turbo-frame example

The simplest turbo-frame example

Turbo frame is a powerful feature of Hotwire, here is a quick memo about how to follow conventions in the simplest case. Convention over configuration is powerful, but sometimes it is also a problem when you are not sure anymore about conventions ;) so I plan to release more "simplest *** Hotwire feature".