Rails 7 with Vite + Stimulus + Tailwind
Rails 7 comes with the importmap
but still, Vite is used popularly for Instant Server Start
and Lightning Fast HMR
features. It uses esbuild
(fast pre-bundler) and rollup
(bundler) under the hood leveraging their strengths for both dev and prod environments.
Also, Building and cleaning Vite assets are automatically integrated with assets: precompile
and assets:clean
, so deploying is straightforward.
Step 1: Installing Vite
gem "vite_rails"
bundle install
bundle exec vite install
The vite install
will generate the following files
# Procfile for dev env to start both rails and vite server
Procfile.dev
vite: bin/vite dev
web: bin/rails s
# Only Vite entry files - mainly used for imports
app/javascript/entrypoints/
# Executable to start the dev server
bin/vite
# Defines the node dependencies
package.json
package-lock.json
# Configuration for the vite for the app
vite.config.ts
# Setting for the Vite
config/vite.json
- A sample file is created
app/javascriot/entrypoints/application.js
in the web app. - Vite will also add the following tags to the
app/views/layouts/application.html.erb
- vite_javascript_tag: Renders a
<script>
tag referencing a JavaScript file - vite_typescript_tag: Renders a
<script>
tag referencing a TypeScript file - vite_stylesheet_tag: Renders a
<link>
tag referencing a CSS file
3. Configure the Vite in the vite.config.ts
file. The basic configuration is like adding RubyPlugin
is done for us by the vite-rails
gem. Apart from this, vite-plugin-full-reload
can be added.
Import using import FullReload from "vite-plugin-full-reload";
Add plugin FullReload(["config/routes.rb", "app/views/**/*"], { delay: 200 })
`vite.config.ts`
import { defineConfig } from 'vite'
import RubyPlugin from 'vite-plugin-ruby'
import FullReload from "vite-plugin-full-reload";
export default defineConfig({
clearScreen: false,
plugins: [
RubyPlugin(),
FullReload(["config/routes.rb", "app/views/**/*"], { delay: 200 })
],
root: "./app/assets",
build: {
manifest: true,
rollupOptions: {
input: "/app/javascript/entrypoints/application.js"
}
}
})
4. Vite also comes with an auto build
feature which enables it to not run the Vite dev server and Vite-rails will automatically detect the option "autoBuild": true
in the vite.json
.
If there is any code happening in the file mentioned in the sourceCodeDir
and watchAdditionalPaths
option, it triggers a build automatically when the asset is requested.
vite.json
{
"all": {
"sourceCodeDir": "app/javascript",
"watchAdditionalPaths": []
},
"development": {
"autoBuild": true,
"publicOutputDir": "vite-dev",
"port": 3036
},
"test": {
"autoBuild": true,
"publicOutputDir": "vite-test",
"port": 3037
}
}
5. Vite builds the assets and stores them in public/vite-dev
and serves the bundled CSS and JS files. The manifest.json
file includes the CSS and Js files along with assets from mainfest-assets.json
Building with Vite ⚡️
vite v4.3.9 building for development...
transforming...
✓ 7 modules transformed.
rendering chunks...
computing gzip size...
../../public/vite-dev/manifest-assets.json 0.00 kB │ gzip: 0.02 kB
../../public/vite-dev/manifest.json 0.30 kB │ gzip: 0.14 kB
../../public/vite-dev/assets/application-23aabef0.css 9.26 kB │ gzip: 2.39 kB
../../public/vite-dev/assets/application-cda856d1.js 43.44 kB │ gzip: 10.95 kB
✓ built in 481ms
Build with Vite complete: /Users/projects/app/public/vite-de
6. Now, Restarting the rails server will output these lines in the browser console. The console.log is from app/javascriot/entrypoints/application.js
the file
Vite ⚡️ Ruby
Visit the guide for more information: <https://vite-ruby.netlify.app/guide/rails>
All set to write JavaScript functionality of the app with Vite! 😃
Step 2: Vite + Stimulus
The Vite.js
has plugins for frameworks like ReactJs, and VueJs. Also, it has a plugin for stimulus framework. In Rails 7, gem 'stimulus-rails'
automatically gets configured. So to combine vite and stimulus, let’s do the following
1. Generate the stimulus files
gem 'stimulus-rails' #ignore if already added by rails
bundle install
rails stimulus:install #Generates the following files
# app/javascript/controllers/index.js
# app/javascript/controllers/application.js
# app/javascript/controllers/hello_controller.js
2. Rails add the initial setup for stimulus in app/javascript/controllers/application.js
and app/javascript/controllers/index.js
. Since we are going to configure the Stimulus using Vite, we can go ahead and comment on all the lines from these two files.
3. Uncomment alsothe following imports from app/javascript/application.js
// import "@hotwired/turbo-rails"
// import "controllers"
4. Add the two Vite plugins that can be used for Stimulus
a) stimulus-vite-helpers — Helpers to easily load all your Stimulus controllers when using Vite.js
b) vite-plugin-stimulus-hmr — HMR for Stimulus controllers in Vite.js
yarn add -D @hotwired/stimulus
yarn add -D stimulus-vite-helpers
yarn add -D vite-plugin-stimulus-hmr
The yarn will add the dependencies to the package.json file.
5. Configure the Vite for stimulus HMR plugin. Now, vite.config.js
is going to look like this
import { defineConfig } from 'vite'
import RubyPlugin from 'vite-plugin-ruby'
import FullReload from 'vite-plugin-full-reload';
import StimulusHMR from 'vite-plugin-stimulus-hmr';
export default defineConfig({
clearScreen: false,
plugins: [
RubyPlugin(),
FullReload(["config/routes.rb", "app/views/**/*"], { delay: 200 }),
StimulusHMR()
],
root: "./app/assets",
build: {
manifest: true,
rollupOptions: {
input: "/app/javascript/entrypoints/application.js"
}
}
}
6. Add the following in the app/javascript/controllers/index.js
. The Stimulus controllers are registered using Vite's globEager and the registerControllers helper
import { Application } from '@hotwired/stimulus'
import { registerControllers } from 'stimulus-vite-helpers'
const application = Application.start()
const controllers = import.meta.globEager('./**/*_controller.js')
registerControllers(application, controllers)
7. Import the Stimulus controller on Vite’s entry point file.
Add the following in app/javascript/entrypoints/application.js
import '../controllers'
8. To test the hello stimulus controller working, add the following in the app/views/layouts/application.html.erb
<div data-controller="hello">
<input data-hello-target="name" type="text">/</input>
<input data-hello-target="output" type="text"></input>
<button data-action="click->hello#greet">Greet</button>
</div>
We are all set with Stimulus that can be bundled using the Vite
Step 3: Vite + Tailwind
- Add the tailwind and other necessary packages using yarn
yarn add -D tailwindcss @tailwindcss/forms @tailwindcss/typography @tailwindcss/aspect-ratio @tailwindcss/forms @tailwindcss/line-clamp autoprefixer
yarn add -D eslint prettier eslint-plugin-prettier eslint-config-prettier eslint-plugin-tailwindcss path
2. Create a tailwind.config.js
configuration file under the project root.
npx tailwindcss init
Add the following code to the tailwind.config.js
/** @type {import('tailwindcss').Config} */
const colors = require('tailwindcss/colors')
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
content: [
'./app/helpers/**/*.rb',
'./app/assets/stylesheets/**/*.css',
'./app/views/**/*.{html,html.erb,erb}',
'./app/javascript/components/**/*.js',
],
theme: {
fontFamily: {
'sans': ["BlinkMacSystemFont", "Avenir Next", "Avenir",
"Nimbus Sans L", "Roboto", "Noto Sans", "Segoe UI", "Arial", "Helvetica",
"Helvetica Neue", "sans-serif"],
'mono': ["Consolas", "Menlo", "Monaco", "Andale Mono", "Ubuntu Mono", "monospace"]
},
extend: {
},
},
corePlugins: {
aspectRatio: false,
},
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/forms'),
require('@tailwindcss/aspect-ratio'),
require('@tailwindcss/line-clamp'),
],
}
3. Replace the contents of app/assets/stylesheets/application.css
with the following
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
4. Create a file named application.css
in app/javascript/entrypoints/
and import the asset's stylesheets
@import "../../assets/stylesheets/application.css";
5. Create a file named postcss.config.js
in the project root
touch postcss.config.js
Add the following
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
6. In app/views/layouts/application.html.erb
,
use the vite_client_tag
, vite_stylesheet_tag
and vite_javascript_tag
instead of the default js and css tags.
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= vite_client_tag %>
<%= vite_stylesheet_tag 'application', data: { "turbo-track": "reload" } %>
<%= vite_javascript_tag 'application' %>
7. Make sure that the compiled assets are delivered to the browser using the Vite by inspecting the page.
<link rel="stylesheet" href="/vite-dev/assets/application-23aabef0.css" data-turbo-track="reload">
<script src="/vite-dev/assets/application-cda856d1.js" crossorigin="anonymous" type="module"></script>
8. If needed, run the Vite server to get the tailwind styling applied
bin/vite dev - clobber
We are set to write the app-related JS and CSS code and let the Vite bundle these assets for our app!