医療機関基盤グループでエンジニアをしている堀です。
私が携わるシステムでRailsのバージョンのEOLのためにバージョンアップする必要性がありました。その際にフロントエンド開発用に導入していたShakapackerがすんなりとアップデート対応できず、また使い勝手が悪かったこともありViteに移行することにしました。移行に際してHMRを実現するのに少々手こずりましたので、そこで得れた知見の共有をさせてください。
なおDockerやRailsといったスキルについてある程度理解されてる方を対象に書いています。ご了承ください。
1. HMR (Hot Module Replacement)とは?
HMRを使うと、TypeScript/JavaScript/SCSSといったフロントエンドで開発中のソースの変更をリアルタイムでブラウザに反映することができます。このため編集結果を確認しながらサクサクとプログラムを進めることができるようになります。

2. 環境の構築
dockerでrailsコンテナとvite_devコンテナをそれぞれ立ち上げて、ローカルPCで編集したソースをリアルタイムにブラウザで確認できる環境をサンプルとして作ってみました。

バージョンは以下の通り。
- Gemモジュール
- Rails 7.2.x
- vite_rails 3.0.x
- Nodeモジュール
- vite 6.3.x
- vite-plugin-ruby 5.1.x
- @vitejs/plugin-react 4.5.x
- ※フロントエンドにReactを使っているため必要
細かい構築手順については検索するとたくさん出てきますので割愛します。以降ではポイントのみ記載します。
2-1. コンテナの設定
RailsサーバーとViteサーバーのコンテナを設定した compose.yml になります。
services: vite_dev: build: . volumes: - .:/myapp - node_modules_cache:/myapp/node_modules ports: - "3036:3036" environment: DEBUG: '*vite*' RAILS_ENV: development VITE_RUBY_HOST: 0.0.0.0 __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: vite_dev command: - /bin/sh - -c - | npm install /myapp/bin/vite dev rails: build: . volumes: - .:/myapp - bundle_data:/usr/local/bundle - tmp_shared:/tmp/shared - cache:/cache depends_on: - vite_dev ports: - "3000:3000" environment: RAILS_ENV: local_dev VITE_RUBY_HOST: vite_dev VITE_RUBY_PORT: 3036 VITE_RUBY_MODE: development command: - /bin/bash - -c - | chmod 777 /tmp/shared rm -f /myapp/tmp/pids/server.pid bundle install bundle exec rails db:migrate bin/rails s -p 3000 -b 0.0.0.0 volumes: bundle_data: tmp_shared: cache: node_modules_cache:
Railsプロジェクトをおいたディレクトリからコンテナを立ち上げて、プログラムの修正はホストマシンのエディタで行う想定です。なお、ローカル環境のRAILS_ENV はlocal_devとしています。
■ vite_devコンテナ
- 3036ポートでコンテナ起動時に Viteサーバーを立ち上げる
node_modulesディレクトリはヴォリュームを作成して永続化RAILS_ENVでdevelopmentを指定するとviteサーバーがdevelopmentモードで起動されるRAILS_ENVを使うとわかりづらい場合はNODE_ENVで設定してもよい__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTSにvite_devコンテナのサービス名を指定- この設定がないとcurlコマンドでviteサーバーの疎通確認の際にNOT ALLOWEDエラーとなる(※viteサーバーとの接続確認の項目で後述)
■ railsコンテナ
- 3000ポートでコンテナ起動時に Railsサーバーを立ち上げる
- 環境変数
VITE_RUBY_HOSTとVITE_RUBY_PORTを設定してvite_devコンテナをViteサーバーに指定 - 環境変数
VITE_RUBY_MODEを development にする- この設定がないと
RAILS_ENVの値が使われてしまう - 設定しとかないとViteサーバーと接続できずに
vite_react_refresh_tagタグが期待通りに出力されない
- この設定がないと
2-2. Viteサーバーの設定
Viteサーバーの設定は config/vite.json と vite.config.js でします。Vite Rubyの公式サイトの説明を参考にします。
■ config/vite.json
{ "all": { "sourceCodeDir": "app/javascript", "watchAdditionalPaths": [] }, "development": { "autoBuild": true, "publicOutputDir": "vite-dev" }, "test": { "autoBuild": true, "publicOutputDir": "vite-test" } }
■ vite.config.js
import { defineConfig } from 'vite' import RubyPlugin from 'vite-plugin-ruby' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [ RubyPlugin(), react() ], server: { host: '0.0.0.0', port: 3036, strictPort: true, hmr: { host: 'localhost', } }, })
3. 設定の際の注意点
ここからは作業を進めるうえで個人的に気になった箇所とハマった箇所について書きます。
3-1. VITE_RUBY_MODEの設定
railsコンテナでは環境変数にVITE_RUBY_MODEでdevelopmentを指定しました。これはvite_devコンテナで立ち上がってるViteサーバーと接続するために必要な設定となります。VITE_RUBY_MODEの設定がないとRAILS_ENVの値が使われますので、stagingやlocal_devといった RAILS_ENV を使っている場合はVITE_RUBY_MODEでdevelopmentの指定がないとviteサーバーと接続できません。
また蛇足ですが、Viteのbuildコマンドで本番環境のデプロイをするときはproductionモードでの実行となります。 Staging環境のようにRAILS_ENVがproduction以外でViteサーバーを立ち上げずにビルド後のファイルを提供したい時があると思います。そのときはVITE_RUBY_MODEにproductionで指定すると良いでしょう。
VITE_RUBY_MODEの設定について公式ページにも記載が見当たらないですが(2025/6/8現在)、サーバーにデプロイするうえでは知っておいた方が良いと思います。
3-2. gem vite:installでインストールされるモジュール
Gemモジュール vite_rails をインストールした後にviteのセットアップをするために一度だけ実行するのがgem vite:installです。
これを実行すると以下のファイルが新規作成もしくは編集されます。
- bin/vite
- app/views/application.html.erb
- config/vite.json
- vite.config.js
また必要なnodeモジュールもインストールされてデフォルトバージョンのものがインストールされます。
もしViteとは関係なくnodeモジュールのアップデートをしたあとにこのコマンドを実行するとバージョンが元に戻ることがあるので気をつけましょう。私は最初 compose.yml でviteコンテナの起動スクリプトにgem vite:installを含めていたためフロントエンドがうまく動かずにハマりました(汗)
3-3. viteサーバーとの接続確認
config/vite.jsonとvite.config.jsで設定された内容が正しく反映されているか、railsコンテナからvite_devコンテナに接続できるかは以下で確認できます。
# rails c > ViteRuby.config => #<ViteRuby::Config:0x0000ffff62c3d6a8 @config= {"additional_entrypoints"=>["~/{assets,fonts,icons,images}/**/*"], "asset_host"=>nil, "assets_dir"=>"assets", "auto_build"=>true, "build_cache_dir"=>#<Pathname:/myapp/tmp/cache/vite>, "public_output_dir"=>"vite-dev", "config_path"=>"config/vite.json", "dev_server_connect_timeout"=>0.01, "package_manager"=>"npm", "public_dir"=>"public", "entrypoints_dir"=>"entrypoints", "source_code_dir"=>"app/javascript", "skip_compatibility_check"=>false, "skip_proxy"=>false, "host"=>"vite_dev", "https"=>false, "port"=>3036, "hide_build_console_output"=>false, "vite_bin_path"=>nil, "watch_additional_paths"=>[], "base"=>"", "ssr_build_enabled"=>false, "ssr_entrypoint"=>"~/ssr/ssr.{js,ts,jsx,tsx}", "ssr_output_dir"=>#<Pathname:/myapp/public/vite-ssr>, "mode"=>"development", "root"=>#<Pathname:/myapp>}> > ViteRuby.instance.dev_server_running? => true
またコンテナ間で通信ができるかの確認はcurlコマンドでもできます。(vite_devコンテナで__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTSを設定しておく必要があります)
# curl http://vite_dev:3036 -v #vite_devはコンテナのサービス名 * Trying XX.XX.XX.XX:3036... * Connected to vite_dev (XX.XX.XX.XX) port 3036 (#0) > GET / HTTP/1.1 > Host: vite_dev:3036 > User-Agent: curl/7.88.1 > Accept: */* > < HTTP/1.1 302 Found < Vary: Origin < Location: /vite-dev/ < Date: Sun, 17 Aug 2025 02:08:34 GMT < Connection: keep-alive < Keep-Alive: timeout=5 < Transfer-Encoding: chunked < * Connection #0 to host vite_dev left intact
上記がうまくいかないときは config/vite.json と vite.config.js の設定を見直します。
3-4. Windowsでのファイルシステムの問題
Windows環境でWSL2にdockerをインストールしてる場合はうまくいかないことがありました。
Windowsのフォルダをコンテナにマウントして立ち上げてるとHMRが機能せずに、ソースコードを変更してもブラウザに変更が反映されませんでした。この原因はWSL2の制約でWindowsのファイルシステム上でおきたファイルの変更がコンテナで検知できないのが原因とのことです。なので永続化したボリュームを作成してコンテナでマウントし、Railsのプロジェクトはそのボリュームに保存しました。Windowsからはそのボリュームを共有フォルダとして編集できるようにセットアップしましょう。
4. 最後に
ハマりどころもありましたが無事HMRの設定が終わり快適に開発を進めれるようになりました。HMRはShakapackerで実現していた時より軽やかに動いてる気がします。トランスパイルも早くViteへの印象はかなり良好です。今回ハマった箇所もありますがドキュメント周りの整備が追いつけば解決していくでしょうし、Viteのツールのポテンシャルを考えると今後さらに普及するかもしれません。
さらに、今回の作業を進めるにあたりGeminiを調査に活用することで時短できました。ただGeminiでのアウトプットはインターネット上から確度の高いものを要約してるようで、vite-Rubyの情報に関しては物足りずにOSSのソースを読む必要がありました。こういった点ではエンジニアとしてのスキルが求められる場面はまだまだありますね。ただAIの進化は早いので今後も積極的に使って見極めていくのは大事だと思いました。
今回得れた知見が少しでも誰かの助力になれば幸いです。
JMDCでは、ヘルスケア領域の課題解決に一緒に取り組んでいただける方を積極採用中です! フロントエンド /バックエンド/ データベースエンジニア等、様々なポジションで募集をしています。 詳細は下記の募集一覧からご確認ください。 hrmos.co
まずはカジュアルにJMDCメンバーと話してみたい/経験が活かせそうなポジションの話を聞いてみたい等ございましたら、下記よりエントリーいただけますと幸いです。 hrmos.co
★最新記事のお知らせはぜひ X(Twitter)、またはBlueskyをご覧ください! Tweets by jmdc_tech twitter.com