Rails Engine with Vite
Rails Engine with Vite
Create engine
1
rails plugin new my_engine --mountable
Vite configuration
1
2
spec.add_dependency 'vite_rails'
spec.add_dependency 'vite_ruby'
1
2
3
4
5
6
7
8
9
10
11
12
require "bundler/setup"
+ require 'vite_ruby'
+ ViteRuby.install_tasks
+ ViteRuby.config.root # Ensure the engine is set as the root.
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
load "rails/tasks/engine.rake"
load "rails/tasks/statistics.rake"
require "bundler/gem_tasks"
1
bundle exec vite install
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
require "vite_ruby"
module MyEngine
class Engine < ::Rails::Engine
isolate_namespace MyEngine
delegate :vite_ruby, to: :class
def self.vite_ruby
@vite_ruby ||= ::ViteRuby.new(root: root, mode: Rails.env)
end
# Serves the engine's vite-ruby when requested
initializer "my_engine.vite_rails.static" do |app|
if Rails.application.config.public_file_server.enabled
# this is the right setup when the main application is already
# using Vite for the theme assets.
app.middleware.insert_after ActionDispatch::Static,
Rack::Static,
urls: [ "/#{vite_ruby.config.public_output_dir}" ],
root: root.join(vite_ruby.config.public_dir),
header_rules: [
# rubocop:disable Style/StringHashKeys
[ :all, { "Access-Control-Allow-Origin" => "*" } ]
# rubocop:enable Style/StringHashKeys
]
else
# mostly when running the application in production behind NGINX or APACHE
app.middleware.insert_before 0,
Rack::Static,
urls: [ "/#{vite_ruby.config.public_output_dir}" ],
root: root.join(vite_ruby.config.public_dir),
header_rules: [
# rubocop:disable Style/StringHashKeys
[ :all, { "Access-Control-Allow-Origin" => "*" } ]
# rubocop:enable Style/StringHashKeys
]
end
end
initializer "my_engine.vite_rails_engine.proxy" do |app|
if vite_ruby.run_proxy?
app.middleware.insert_before 0,
ViteRuby::DevServerProxy,
ssl_verify_none: true,
vite_ruby: vite_ruby
end
end
initializer "my_engine.vite_rails_engine.logger" do
config.after_initialize do
vite_ruby.logger = Rails.logger
end
end
end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"all": {
"sourceCodeDir": "app/frontend",
"watchAdditionalPaths": [],
"publicOutputDir": "my_engine-assets"
},
"development": {
"autoBuild": true,
"publicOutputDir": "my_engine-assets-dev",
"port": 3036
},
"test": {
"autoBuild": true,
"publicOutputDir": "my_engine-assets-test",
"port": 3037
}
}
1
rails g controller home index
1
2
3
MyEngine::Engine.routes.draw do
root "home#index"
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
<head>
<title>My engine</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= yield :head %>
+ <%= vite_client_tag %>
+ <%= vite_javascript_tag 'application' %>
<%= stylesheet_link_tag "my_engine/application", media: "all" %>
</head>
<body>
<%= yield %>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
require "vite_rails/version"
require "vite_rails/tag_helpers"
module MyEngine
module ApplicationHelper
include ::ViteRails::TagHelpers
def vite_manifest
::MyEngine::Engine.vite_ruby.manifest
end
end
end
Create host app
1
rails new demo
1
gem "my_engine", path: "path/to/engine"
1
mount MyEngine::Engine => "/my_engine"
TailwindCSS configuration
1
2
yarn add -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./app/views/**/*.rb",
"./app/views/**/*.html.erb",
"./app/views/layouts/*.html.erb",
"./app/helpers/**/*.rb",
"./app/assets/stylesheets/**/*.css",
"./app/frontend/**/*.js",
],
theme: {
extend: {},
},
plugins: [],
}
1
touch app/frontend/entrypoints/application.css
1
2
3
@tailwind base;
@tailwind components;
@tailwind utilities;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<title>My engine</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= yield :head %>
<%= vite_client_tag %>
<%= vite_javascript_tag 'application' %>
+ <%= vite_stylesheet_tag 'application', data: { "turbo-track": "reload" } %>
<%= stylesheet_link_tag "my_engine/application", media: "all" %>
</head>
<body>
<%= yield %>
</body>
</html>
1
2
<h1 class="font-bold text-4xl text-indigo-500">Home#index</h1>
<p>Find me in app/views/my_engine/home/index.html.erb</p>
Vue configuration
1
yarn add -D vue @vitejs/plugin-vue
1
2
3
4
5
6
7
8
9
10
import { defineConfig } from 'vite'
import RubyPlugin from 'vite-plugin-ruby'
import vue from '@vitejs/plugin-vue' // <-------- add this
export default defineConfig({
plugins: [
RubyPlugin(),
vue() // <-------- add this
],
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>
<h1>Vue App!</h1>
</div>
</template>
<script setup>
</script>
<style lang="css" scoped>
h1 {
color: red;
}
</style>
1
<div id="app"></div>
1
2
3
4
5
import { createApp } from 'vue'
import App from '../components/App.vue'
const app = createApp(App)
app.mount('#app')
Pass data from Rails to Vue components
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<h1>{{ msg }}</h1>
</div>
</template>
<script setup>
defineProps({
msg: String
})
</script>
<style lang="css" scoped>
h1 {
color: red;
}
</style>
1
2
3
4
5
6
7
module MyEngine
class HomeController < ApplicationController
def index
@msg = "Hello Vue on Rails!"
end
end
end
1
2
3
4
5
6
7
8
9
10
11
12
<%=
content_tag(
:div,
id: 'appProps',
data: {
props: {
msg: @msg
}
}.as_json
) {}
%>
<div id="app"></div>
it will render the div below on the page.
1
2
3
4
<div
id="appProps"
data-props="{"msg":"Hello Vue on Rails!"}">
</div>
1
2
3
4
5
6
import { createApp } from 'vue'
import App from '../components/App.vue'
const appProps = document.getElementById('appProps').dataset.props
const app = createApp(App, JSON.parse(appProps))
app.mount('#app')
References
- https://github.com/maglevhq/maglev-core
- https://primevise.com/blog/ruby-on-rails-boilerplate-vite-tailwind-stimulus
- https://dev.to/kevinluo201/use-viterails-to-use-vue-sfcsingle-file-component-vue-in-rails7-51bn
This post is licensed under CC BY 4.0 by the author.