Project

rails_vite

0.0
No release in over 3 years
Simple Vite integration for Rails, inspired by Laravel. No proxy, no config duplication.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

>= 7.0
 Project Readme

RailsVite

Gem Version

Vite integration for Rails, inspired by Laravel's Vite plugin. No proxy, no config duplication, no magic.

Table of Contents

  • How It Works
  • Quick Start
  • Usage
  • Vite Config
  • Adding Frameworks
  • SSR
  • Auto Build
  • Testing the Build
  • Custom Paths
  • Rake Tasks
  • Migrating from vite_rails
  • Contributing
  • License
Built by Evil Martians

How It Works

Development: The Vite plugin writes tmp/rails-vite.json with the dev server URL. The Rails helper reads it and emits <script> tags pointing directly at Vite. The browser talks to Vite — Puma never touches your assets.

Production: vite build outputs fingerprinted assets to public/vite/ with a standard Vite manifest. The Rails helper reads the manifest and emits the correct tags.

No Rack proxy. No config/vite.json. No extra binstubs.

Quick Start

Add to your Gemfile:

gem "rails_vite"

Run the install generator:

bundle install
bin/rails generate rails_vite:install

This creates vite.config.ts, installs dependencies, and updates your layout.

Start development:

bin/dev

Usage

In your layout:

<%= vite_tags "application.js" %>

Short names are automatically prefixed with sourceDir (default: app/javascript). Paths containing / are used as-is.

Development output (when tmp/rails-vite.json exists):

<script src="http://localhost:5173/@vite/client" type="module"></script>
<script src="http://localhost:5173/app/javascript/application.js" type="module"></script>

Production output (reads manifest):

<link rel="modulepreload" href="/vite/assets/vendor-b3c4d5e6.js" />
<script src="/vite/assets/application-a1b2c3d4.js" type="module"></script>
<link rel="stylesheet" href="/vite/assets/application-x9y8z7w6.css" />

Helpers

Helper Purpose
vite_tags(*entries, **options) Emits script, stylesheet, and modulepreload tags
vite_javascript_tag(*entries, **options) Same as vite_tags, appends .js to extensionless names
vite_stylesheet_tag(*entries, **options) Same as vite_tags, appends .css to extensionless names
vite_typescript_tag(*entries, **options) Same as vite_tags, appends .ts to extensionless names
vite_asset_path(name) Returns the fingerprinted path from the manifest
vite_image_tag(name, **options) Image tag with manifest-resolved src

All tag helpers accept arbitrary HTML attributes:

<%= vite_tags "application.js", "application.css",
    "data-turbo-track": "reload", nonce: content_security_policy_nonce %>

CSS Entry Points

CSS files are detected by extension and emit <link rel="stylesheet">:

<%= vite_tags "application.css" %>

CSP Nonces

<%= vite_tags "application.js", nonce: content_security_policy_nonce %>

Subresource Integrity (SRI)

SRI lets browsers verify that fetched assets haven't been tampered with by checking cryptographic hashes. Install the vite-plugin-manifest-sri plugin:

npm install -D vite-plugin-manifest-sri
import { defineConfig } from 'vite';
import rails from 'rails-vite-plugin';
import manifestSRI from 'vite-plugin-manifest-sri';

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

That's it — integrity and crossorigin="anonymous" attributes are automatically added to all script, stylesheet, and modulepreload tags when the manifest includes integrity hashes.

Asset Discovery (Images, Fonts)

Use import.meta.glob in your entry point to include assets in the Vite manifest:

// app/javascript/application.js
import.meta.glob(['../assets/images/**']);

Then reference them in views:

<%= vite_image_tag "app/assets/images/logo.png", alt: "Logo" %>

Vite Config

The install generator creates a minimal vite.config.ts:

import { defineConfig } from 'vite';
import rails from 'rails-vite-plugin';

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

Plugin Options

Option Default Description
input auto-detected Entry point(s). If sourceDir/entrypoints/ exists, all files in it are used. Otherwise, detects application.{js,ts,jsx,tsx} in sourceDir
sourceDir 'app/javascript' Source directory. Short names are prefixed with this. Also sets the @ import alias
ssr SSR entry point
ssrOutputDirectory 'ssr' SSR output directory
devMetaFile 'tmp/rails-vite.json' Dev metadata file path
buildDirectory 'vite' Build output subdirectory inside public/
publicDirectory 'public' Public directory
refresh true Paths to watch for full-page reload. true watches app/views/** and app/helpers/**

Multiple Entry Points

rails({
  input: ['application.js', 'admin.js'],
})
<!-- In application layout -->
<%= vite_tags "application.js" %>

<!-- In admin layout -->
<%= vite_tags "admin.js" %>

Custom Source Directory

rails({
  input: ['entrypoints/application.ts', 'entrypoints/admin.ts'],
  sourceDir: 'app/frontend',
})
<%= vite_tags "entrypoints/application.ts" %>

Adding Frameworks

React

npm install -D @vitejs/plugin-react
import { defineConfig } from 'vite';
import rails from 'rails-vite-plugin';
import react from '@vitejs/plugin-react';

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

The React Refresh preamble is injected automatically when @vitejs/plugin-react is detected — no manual setup needed.

Vue

npm install -D @vitejs/plugin-vue
import { defineConfig } from 'vite';
import rails from 'rails-vite-plugin';
import vue from '@vitejs/plugin-vue';

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

SSR

Set ssr to the entry point used for server-side rendering. When you run npx vite build --ssr, the plugin uses this as the input and outputs to the ssrOutputDirectory (default: ssr/).

rails({
  ssr: 'ssr.tsx',
})

Build and run:

npx vite build && npx vite build --ssr
node ssr/ssr.js

Auto Build

When the Vite dev server is not running, rails_vite automatically rebuilds assets on the first request if sources have changed. This is useful for system tests and quick checks without running bin/dev.

Disable it:

# config/initializers/rails_vite.rb
Rails.application.config.rails_vite.auto_build = false

By default, auto build is enabled in development and test (Rails.env.local?).

Note: for parallel test runners, disable auto build and use rake vite:build before the suite instead.

Testing the Build

To verify your production build works in development:

rake vite:build         # build assets
bin/rails s             # start Rails without Vite dev server

Without the Vite dev server running (no tmp/rails-vite.json), Rails serves built assets from public/vite/. To switch back to dev mode, start Vite again — the dev metadata takes priority.

Clean up built assets with rake vite:clobber.

Custom Paths

If you override build.outDir in vite.config.ts, tell the gem where to find things:

# config/initializers/rails_vite.rb
Rails.application.config.rails_vite.manifest_path = Rails.root.join("public/custom/manifest.json")
Rails.application.config.rails_vite.asset_prefix = "/custom"

Defaults match the plugin defaults — no config needed if you follow conventions.

Rake Tasks

Task Description
rake vite:build Build assets for production
rake vite:install Install JavaScript dependencies
rake vite:clobber Remove public/vite/

vite:build hooks into assets:precompile and test:prepare automatically. Skip with SKIP_VITE_BUILD=1.

Migrating from vite_rails

1. Swap dependencies

# Gemfile
- gem "vite_rails"
+ gem "rails_vite"
// package.json — replace vite-plugin-ruby with rails-vite-plugin
- "vite-plugin-ruby": "^5.1.1"
+ "rails-vite-plugin": "^0.1.0"

2. Replace vite.config.ts

import { defineConfig } from 'vite';
import rails from 'rails-vite-plugin';

export default defineConfig({
  plugins: [
    rails({
      sourceDir: 'app/frontend',
    }),
  ],
});

If you have an entrypoints/ directory inside sourceDir, all files in it are auto-discovered — no need to list them. Otherwise, set input explicitly.

3. Delete files

  • config/vite.json — settings now live in vite.config.ts
  • bin/vite — no longer needed, Procfile.dev runs npx vite directly

4. Update layouts

Remove vite_client_tag and vite_react_refresh_tag — both are automatic now.

The vite_javascript_tag, vite_stylesheet_tag, and vite_typescript_tag helpers work as drop-in replacements:

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/skryukov/rails_vite.

License

The gem is available as open source under the terms of the MIT License.