Opal-Vite-Rails
Seamless integration of Opal (Ruby to JavaScript compiler) with Vite in Rails applications. Write Ruby code that runs in the browser with the fast development experience of Vite.
Features
- ✅ Ruby in the Browser: Write Ruby code that compiles to JavaScript
- ✅ Vite Integration: Fast HMR (Hot Module Replacement) during development
- ✅ Rails Integration: Seamless integration with Rails views and asset pipeline
- ✅ Source Maps: Debug Ruby code directly in browser DevTools
- ✅ Production Ready: Optimized builds with manifest-based asset resolution
Requirements
- Ruby >= 3.0
- Rails >= 7.0
- Node.js >= 18.0
- Opal >= 1.8
Installation
Add to your Gemfile:
gem 'opal-vite-rails'
Install the gem:
bundle install
Run the generator:
rails generate opal_vite:install
This will:
- Create
app/opal/directory for your Ruby code - Generate
app/opal/application.rbentry point - Configure Vite with the Opal plugin
- Create an example controller and view
- Add necessary routes
Install JavaScript dependencies:
npm install vite-plugin-opal
# or
pnpm install vite-plugin-opal
Usage
Development Mode
Start the Vite development server in one terminal:
bin/vite dev
Start Rails in another terminal:
rails server
Visit http://localhost:3000/opal_demo to see Opal in action!
Writing Opal Code
Create Ruby files in app/opal/:
# app/opal/hello.rb
require 'native'
puts "Hello from Ruby running in the browser!"
# Use JavaScript via backticks
`
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM is ready!');
const element = document.getElementById('my-element');
if (element) {
element.textContent = 'Updated by Ruby!';
}
});
`
# Or use the Native module for cleaner JS interop
class MyComponent
def initialize(element_id)
@element = Native(`document.getElementById(#{element_id})`)
end
def update(text)
@element[:textContent] = text
end
end
MyComponent.new('my-element').update('Hello from Ruby!')
Using in Views
Add Opal JavaScript to your views with the helper:
<!-- app/views/welcome/index.html.erb -->
<div id="my-element">Loading...</div>
<%= opal_javascript_tag "hello" %>
The helper automatically handles development vs production modes:
- Development: Loads from Vite dev server with HMR
- Production: Loads precompiled assets from manifest
View Helpers
opal_javascript_tag
Loads an Opal JavaScript bundle:
<%= opal_javascript_tag "application" %>
<%= opal_javascript_tag "application", defer: true %>
<%= opal_javascript_tag "application", type: "module" %>
opal_asset_path
Gets the path to an Opal asset:
<script src="<%= opal_asset_path('application.js') %>"></script>
vite_running?
Checks if Vite dev server is running:
<% if vite_running? %>
<p>Development mode with HMR enabled</p>
<% else %>
<p>Production mode</p>
<% end %>
Requiring Other Files
Organize your code with require:
# app/opal/lib/calculator.rb
class Calculator
def add(a, b)
a + b
end
end
# app/opal/application.rb
require 'lib/calculator'
calc = Calculator.new
puts calc.add(5, 3) # => 8
Production Deployment
Compile Opal assets:
rake opal_vite:compile
This runs Vite build and creates optimized bundles in public/vite/.
The compile task is automatically added to rake assets:precompile, so it runs during deployment on platforms like Heroku.
Rake Tasks
# Compile Opal assets for production
rake opal_vite:compile
# Clean compiled assets
rake opal_vite:clean
# Show configuration info
rake opal_vite:info
Configuration
Configure in config/application.rb or environment files:
# config/application.rb
config.opal_vite.source_path = "app/opal" # Default
config.opal_vite.public_output_path = "vite" # Default
Project Structure
app/
├── opal/
│ ├── application.rb # Entry point
│ ├── application_loader.js # JS loader (required for Vite)
│ └── lib/
│ └── my_module.rb # Your Ruby modules
├── controllers/
└── views/
vite.config.ts # Vite config with opal plugin
How It Works
Development:
- Vite dev server watches
.rbfiles - When a
.rbfile changes, Opal compiles it to JavaScript - HMR updates the browser instantly
- Source maps allow debugging Ruby code in DevTools
- Vite dev server watches
Production:
rake opal_vite:compilebuilds optimized JavaScript bundles- Manifest file maps logical names to hashed filenames
- Rails helpers load assets from the manifest
Advanced Usage
Custom Vite Configuration
Customize vite.config.ts:
import { defineConfig } from 'vite'
import RubyPlugin from 'vite_ruby/plugins/ruby'
import opal from 'vite-plugin-opal'
export default defineConfig({
plugins: [
RubyPlugin(),
opal({
loadPaths: ['./app/opal', './lib/opal'],
sourceMap: true,
debug: process.env.NODE_ENV === 'development'
})
],
// Your custom Vite config...
})
Using Opal Standard Library
Opal includes a standard library with Ruby core classes:
require 'native' # JavaScript interop
require 'promise' # Promise support
require 'json' # JSON parsing
require 'set' # Set class
require 'ostruct' # OpenStruct
# ... and more
Troubleshooting
HMR not working
Make sure:
- Vite dev server is running (
bin/vite dev) - You're using the JavaScript loader pattern (
.rbfiles imported via.jsloaders) - Rails is configured to proxy to Vite in development
Assets not loading in production
Run the compile task before deployment:
RAILS_ENV=production rake opal_vite:compile
Source maps not showing Ruby code
Ensure sourceMap: true in vite.config.ts:
opal({
sourceMap: true,
// ...
})
Examples
See the examples/rails-app directory for a complete working example.
Contributing
Bug reports and pull requests are welcome on GitHub.
License
The gem is available as open source under the terms of the MIT License.
See Also
- Opal - Ruby to JavaScript compiler
- Vite - Next generation frontend tooling
- vite_ruby - Vite integration for Ruby
- vite-plugin-opal - Vite plugin for Opal