Skip to content

Instantly share code, notes, and snippets.

@thestephenmarshall
Last active February 2, 2024 21:13
Show Gist options
  • Save thestephenmarshall/1edae4c612ad6dce99775a84b73400d0 to your computer and use it in GitHub Desktop.
Save thestephenmarshall/1edae4c612ad6dce99775a84b73400d0 to your computer and use it in GitHub Desktop.
Moving from Webpacker to Vite in a Rails Application

Migrating from Webpacker to Vite

Since Webpacker is used in Rails apps, you would want not only to add vite from NPM, but also the ancillary dependencies.

Easiest Way to Get Started

The easiest way to do this is to 1) add the vite_ruby gem to your Gemfile or gemspec, then run bundle, then 2) run bundle exec vite install. /This will take care of adding the other dependencies needed to get started./

Results

Following the above steps will do these things for your app:

  • Install necessary dependencies to begin using vite with Rails, which includes some nice helpers
  • Infer that app/javascript is your JS/TS root source directory and create a configuration for that named vite.json
  • Generate a vite.config.ts file which is where you can make use of Vite plugins and configure how things are compiled.
  • Generate (based on the root source directory config) an entrypoints directory which is essentially the same as the packs directory for Webpacker.
    • Generate application.js and application.css files inside of this directory which are intended to be the main entry points for your application JS/TS & CSS.

Do I Have to Move Everything Into the Entrypoints?

Although it will be easiest to do this, you do not have to move everything into the app/javascript/entrypoints directory.

To keep your components where they are, you simply need to add the additionalEntrypoints list to vite.json like so:

    "additionalEntrypoints": [
      "app/javascript/components/**/*.tsx",
    ]

This will make things easier for the transition and will allow Vite to find and compile any source that is matched. (Note: typical glob rules apply)

Making Import Aliases for Easier Importing

Like webpack, you can create aliases just the same way to make imports like this:

import Foo from '../../../helpers/foo'

into:

import Foo from 'helpers/foo'

This can be configured in vite.config.ts like so:

export default defineConfig({
  ...
  resolve: {
    alias: {
      'helpers': resolve(__dirname, 'app/javascript/helpers'),
    }
  }
})

Taking the Trash Out 🗑️

So now you have a whole bundle of Webpacker assets that no longer need to be in your app. You will want to remove the libraries:

  • webpacker
  • react_rails (see below for alternative)
  • @babel/preset-env and family from .babel.config.js
  • @rails/webpacker
  • webpack
  • any webpack loaders
  • react_ujs we didn’t need this
  • webpack-dev-server
  • anything else with webpack in it’s name 🔥

Also, delete these directories:

  • config/webpacker.yml
  • config/webpacker
  • app/javascript/packs - move any other entries here to the entrypoints directory

Ensure Your Layout(s) Contain Vite Asset Tags

As mentioned here, vite_ruby provides nice helpers to load JS/TS and (S)CSS.

These are likely the tags you will need for your app to properly function:

  • vite_stylesheet_tag “application.scss”
  • vite_typescript_tag “application.ts”
  • vite_client_tag

Your First Vite Enabled View

For your first view, choose something simple and add a tag for it in the view (*assuming ERB here and assuming you have created an alias for “components” or the directory is under entrypoints):

<%= vite_typescript_tag "components/Foo/index" %>

This will place the JavaScript tag in the HTML output of the view and the extension, in this case, will be .ts

No need to worry! This is simply a field in the Vite manifest entry which, depending on how your component is structured, will have one or more linked file names inside. This is the “recipe” for your component and all recipes contain ingredients or “assets” in our case. The result will be compiled JS/CSS which will execute/render in the browser as expected.

Hot Module Reloading

We were not able to get this properly working (yet) likely due to conflicts with Turbolinks. It isn’t a critical feature and can be pursued any any time. Failed HMR will simply be full page reload.

Replacing react_rails

Normally, you would use react-rails like so (assuming ERB)

<%= react_component("Foo/index", @props) %>

Which spits out a data infused <div> into wherever the tag resides within the HTML hierarchy. Following this, the library runs a script that locates each <div> in the DOM tree then applies the parsed JSON as props to each applicable React component via the react-dom library.

react-rails doesn’t support Vite.js, so we created a WIP alternative which does the same.

This alternative is two-part, so the client-side part is found here

As you can see, we are relying on Turbolinks events in the above example to fire before we query the DOM to find any applicable React components to hydrate.

More optimization will come!

Summary

In my brief overview, I explained the different steps to replacing Webpacker in your app with Vite.js. This guide isn’t intended as a complete step-by-step instruction manual, but does highlight most of the main features needed to get your app up and running quickly. If you have any questions, feel free to reach out!


Stephen (@thestephenmarshall)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment