Setting up a new Rails 7 app with Vite, Inertia, and Svelte
Tags: rails, vite, svelte
Published: 2021-12-22 11:45:08

[Rails 7 has been released](https://rubyonrails.org/2021/12/15/Rails-7-fulfilling-a-vision) and [Webpacker is being phased out](https://www.simplethread.com/rails-7-the-end-of-webpacker/).

And if you're anything like me and prefer to use HMR and component frameworks over [Hotwire](https://hotwired.dev) and importmaps, you might want to look for a Webpacker alternative now. I found [Vite Ruby](https://vite-ruby.netlify.app/) to be a capable replacement, which also (supposedly) makes your development flow [more faster](https://vitejs.dev/guide/why)™. 

So let's take a look at how to set up a new Rails 7 app using my favorite frameworks out there ([Inertia](https://inertiajs.com) and [Svelte](https://svelte.dev)) and bundle it all up with Vite. 

## Let's go

Start off by initializing a new Rails 7 app without Javascript and asset pipeline:

```
rails new app --skip-javascript --skip-asset-pipeline
```

Once this is done, add the `inertia_rails` and `vite_rails` gems:

```
bundle add inertia_rails
bundle add vite_rails
```

The `vite_rails` gem adds an installer to your project. Run it with

```
bundle exec vite install
```

This generates default vite config files, a `frontend` directory, and also updates your layout file to include the vite bundle. It will also setup our initial package.json. We'll need more packages. Add them:

```
npm install -D axios svelte @sveltejs/vite-plugin-svelte @inertiajs/svelte
``` 

> IMPORTANT UPDATE:
> Since vite-plugin-svelte version 2.0, it is required that your package.json contains `"type": "module"`. You will have to add this manually if it doesn't already.

Then find `application.js` in our new `app/frontend/entrypoints` directory and replace its contents with:

```js
import axios from 'axios'

import { createInertiaApp } from '@inertiajs/svelte'


const pages = import.meta.glob('../pages/**/*.svelte')

const csrfToken = document.querySelector('meta[name=csrf-token]').content
axios.defaults.headers.common['X-CSRF-Token'] = csrfToken



createInertiaApp({ 
  resolve: name => pages[`../pages/${name}.svelte`](),
  setup({ el, App, props }) {
    new App({ target: el, props })
  },
})

```

Next, open up your `vite.config.ts` file and change it like so:
```js
import { defineConfig } from 'vite'
import RubyPlugin from 'vite-plugin-ruby'
import { svelte } from '@sveltejs/vite-plugin-svelte';

export default defineConfig({
  resolve: {
    dedupe: ['axios']
  },
  plugins: [
    RubyPlugin(),
    svelte()
  ]
})


```
This config file ensures that axios is only included once in your bundle, and Inertia and your own imports will actually resolve to the same axios.

Now you can place your Inertia page components into `app/frontend/pages`, start your rails server with `rails s`, the dev server with `./bin/vite dev`, and it should all work as usual. 

### Bonus: Persistent Layouts

In the example above you might have noticed the lack of a [persistent layout](https://inertiajs.com/pages#persistent-layouts) - a parent component that won't change upon navigation. To add a persistent layout, update your `application.js` like this:

```js
import axios from 'axios'

import Layout from '../pages/_layout.svelte'

import { createInertiaApp } from '@inertiajs/svelte'

const pages = import.meta.glob('../pages/**/*.svelte')

const csrfToken = document.querySelector('meta[name=csrf-token]').content
axios.defaults.headers.common['X-CSRF-Token'] = csrfToken

createInertiaApp({ 
  resolve: async name => {
    const page = await pages[`../pages/${name}.svelte`]()
    return Object.assign({layout: Layout}, page)
  },
  setup({ el, App, props }) {
    new App({ target: el, props })
  },
})

```

Now you can use persistent layouts with Inertia and Vite.

Happy coding 😊