Article originally published here : https://www.bootrails.com/blog/vitejs-rails-a-wonderful-combination/
vite_rails is a fantastic gem that completely drops the need for jsbundling, Webpacker, importmaps and/or Sprockets.
0. Motivation
We are building bootrAils on top of the last Rails version. When Rails 7 came out, it was (kind of) a relief to see a replacement for Webpacker. Whereas the new assets pipeline looks better, it seemed that room for even better could exist somewhere.
The current 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...
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
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
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. 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 !
Top comments (0)