Skip to content
On this page

Migrating to Vite

If you would like to add a note about other setups, pull requests are welcome!

Starting Fresh ☀️

When starting a new project, follow the guide, and you should have a basic setup where you can place your JavaScript, stylesheets, and other assets.

Sprockets ⚙️

If you would like to remove sprockets and use Vite Ruby alone, you can follow this example.

Have in mind that this is optional, as both approaches can coexist without issues.

Webpacker 📦

When migrating from Webpacker, start by following the guide to get a basic setup working before proceeding to migrate existing code.

During installation, Vite Ruby detect if the app/javascript directory exists, and use that in your config/vite.json instead of the default.

  "all": {
    "sourceCodeDir": "app/javascript",

One entry at a time

The recommended approach for medium-to-large-sized applications is to migrate one entrypoint at a time if possible. Gradually move each file in app/javascript/packs (managed by Webpacker) to app/javascript/entrypoints (managed by Vite Ruby).

Check this migration from Webpacker as an example.

Proceed to fix any errors that occur (i.e. differences between Webpack and Vite.js) by checking the Troubleshooting section and the following recommendations:

  • Explicitly add a file extension to any non-JS imports.

    - import TextInput from '@/components/TextInput'
    + import TextInput from '@/components/TextInput.vue'

    The same is true, when importing .svelte or .scss files from Javascript.

  • Replace usages of tag helpers as you move the entrypoints.

    + <%= vite_client_tag %>
    - <%= stylesheet_pack_tag 'application' %>
    - <%= javascript_packs_with_chunks_tag 'application' %>
    + <%= vite_javascript_tag 'application' %>
    - <%= stylesheet_pack_tag 'mobile' %>
    + <%= vite_stylesheet_tag 'mobile' %>
    - <img src="<%= asset_pack_path('images/logo.svg') %>">
    + <img src="<%= vite_asset_path('images/logo.svg') %>">
  • Replace require.context with import.meta.glob or import.meta.globEager.

    - const context = require.context("./controllers", true, /\.js$/)
    + const controllers = import.meta.globEager('./**/*_controller.js')

    If you want to automatically register the Stimulus Controllers, have a look at stimulus-vite-helpers as a replacement for @hotwired/stimulus-webpack-helpers

  • If importing code that is located outside of the sourceCodeDir, make sure to add a glob expression in watchAdditionalPaths, so that changes to these files are detected, and trigger a recompilation.

  • If you were using rails-erb-loader, you might want to check vite-plugin-erb to ease the transition, but it's better to avoid mixing ERB in frontend assets.

  • Make sure npx is available (comes by default in most node.js installations), or clear the vite:install_dependencies rake task and provide your own implementation.

  • If you are importing your own source code without absolute path prefix (such as @/ or ~/) you can either prefix all imports with @/:

    - import MyModule from "admin/components/MyModule.vue"
    + import MyModule from "@/admin/components/MyModule.vue"
    Or you can define an alias for every folder under sourceCodeDir:
    // vite.config.js
    import path from 'path';
    import fs from 'fs'
    const sourceCodeDir = "app/javascript"
    const items = fs.readdirSync(sourceCodeDir)
    const directories = items.filter(item => fs.lstatSync(path.join(sourceCodeDir, item)).isDirectory())
    const aliasesFromJavascriptRoot = {}
    directories.forEach(directory => {
      aliasesFromJavascriptRoot[directory] = path.resolve(__dirname, sourceCodeDir, directory)
    export default defineConfig({
      resolve: {
        alias: {
          // can add more aliases, as "old" images or "@assets", see below
          images: path.resolve(__dirname, './app/assets/images'),

Loaders to Plugins

Vite provides many features out of the box, which reduce the need for configuration. For example, to use SCSS just install sass, and Vite will detect the package and use it to process .scss files.

In complex setups, the app might depend on specific webpack loaders, which can't be used in Vite, which uses Rollup under the hood.

Check Vite Rollup Plugins and Awesome Vite to find equivalent plugins.

Assets 🎨

It's recommended that you locate assets under the sourceCodeDir, which requires no additional configuration. You can use import aliases to easily reference them.

However, during a migration it can be convenient to reference files that are still being used through the assets pipeline, such as fonts in app/assets.

In that case, you should add app/assets/**/* to watchAdditionalPaths to track changes to these files and trigger a rebuild when needed.

In order to reference these files it's highly recommended to define your own import aliases:


import { resolve } from 'path'
import { defineConfig } from 'vite'

export default defineConfig({
  resolve: {
    alias: {
      '@assets': resolve(__dirname, 'app/assets'),

Remember to update any references to these files, for example, in stylesheets processed by Vite:

@font-face {
  font-family: 'OpenSans';
- src: font-url('OpenSans.woff2');
+ src: url('@assets/fonts/OpenSans.woff2');

Once you have finished the migration, you can move all assets under sourceCodeDir:

@font-face {
  font-family: 'OpenSans';
- src: url('@assets/fonts/OpenSans.woff2');
+ src: url('@/fonts/OpenSans.woff2');
Migrating to Vite has loaded