【2025年最新版】Ruby on Rails + Vite + React + TypeScript の開発環境を作る方法
2025年のWeb開発、みなさんはどんな開発環境で進めていますか?
Ruby on Railsは今でも強力なWebフレームワークとして君臨していますが、フロントエンド開発の世界はどんどん進化しています。特にTypeScriptとReactの組み合わせは、もはや現場では当たり前の選択になってきています。
そんな中で注目したいのが「Vite」です。従来のWebpackと比べると、圧倒的に高速な開発サーバーとHMR(ホットリロード)機能を提供してくれます。実際の開発現場では、この「爆速」な開発体験がかなり重宝されています。
今回は、RailsにVite、React、TypeScriptを組み合わせた最新の開発環境の作り方を紹介します。2025年の現在でも、この構成は非常に使いやすく、効率的な選択肢の一つです。
それでは、環境構築から実際のコンポーネント作成まで、順番に見ていきましょう!
Railsプロジェクト作成
rails new generative-ai-rails -c tailwind --skip-JS
ここで一旦サーバーを起動して
ちゃんと起動できている。
トップページの作成
class HomeController < ApplicationController
def index
# TODO: ホーム画面の表示
end
end
routes.rb
Rails.application.routes.draw do
# ...
get "up" => "rails/health#show", as: :rails_health_check
# ...
root "home#index"
end
views/home/index.html.erb
<h1>GenerativeAiRails, Hello World!</h1>
再度localhostにアクセスしてトップページが作れていることを確認する。
vite_rails インストールとviteの設定
TypeScriptのトランスパイルに必要なvite をインストール
bundle add vite_rails
bundle exec vite install
application.html.erb
<!DOCTYPE html>
<html>
<head>
<title><%= content_for(:title) || "Generative Ai Rails" %></title>
...
<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
<%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
<%= vite_client_tag %>
<%= vite_javascript_tag 'application' %>
<!--
If using a TypeScript entrypoint file:
vite_typescript_tag 'application'
If using a .jsx or .tsx entrypoint, add the extension:
vite_javascript_tag 'application.jsx'
Visit the guide for more information: https://vite-ruby.netlify.app/guide/rails
-->
</head>
<body>
<main class="container mx-auto mt-28 px-5 flex">
<%= yield %>
</main>
</body>
</html>
foreman の導入
bundle add foreman
Procfile.dev はすでに作成されているので、
bundle exec foreman start -f Procfile.dev
でwebサーバーとviteのHMR用サーバーが立ち上がる。
この状態で、たとえば app/javascript/entrypoints/application.js のconsole.logの文言を変更すると、自動でブラウザが更新されて変更が反映される。
React + TypeScript の導入
eslint.config.js
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)
vite.config.ts
import { defineConfig } from "vite";
import RubyPlugin from "vite-plugin-ruby";
import react from "@vitejs/plugin-react";
import path from "path";
export default defineConfig({
build: {
manifest: true,
rollupOptions: {
input: "./app/javascript/entrypoints/application.tsx",
},
},
plugins: [RubyPlugin(), react()],
server: {
hmr: true,
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./app/javascript"),
},
},
});
tsconfig.json
{
"files": ["app/javascript/entrypoints/application.tsx"],
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": ["./app/javascript/*"]
}
},
"include": ["app/javascript/**/*"]
}
package.json
{
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@types/node": "^22.10.7",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.17.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.16",
"globals": "^15.14.0",
"typescript": "~5.6.2",
"typescript-eslint": "^8.18.2",
"vite": "^6.0.5",
"vite-plugin-ruby": "^5.1.1"
}
}
npm run build を行なって正常にビルドできるか確認する
application.html.erb の修正
vite 関連のタグを以下のように修正する。
vite_javascript_tagをvite_typescript_tag に変更し、それが .tsx を読み込む場合には拡張子まできちんと表記すること。
application.html.erb
<% if Rails.env.development? %>
<%= vite_client_tag %>
<%= vite_react_refresh_tag %>
<% end %>
...
<%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
<%= vite_typescript_tag 'application.tsx' %>
実際にReact Componentを書いてみる
views/home/index.erb.html
<div id="root"></div>
javascript/components/HelloWorld.tsx
export default function HelloWorld() {
return <h1>Hello World</h1>;
}
javascript/entrypoint/application.tsx
import { createRoot } from "react-dom/client";
import HelloWorld from "@/components/HelloWorld";
console.log("Vite ⚡️ Rails ⚡️ Foreman");
document.addEventListener("DOMContentLoaded", () => {
const root = document.getElementById("root");
if (root) {
createRoot(root).render(<HelloWorld />);
}
});
この状態でサーバーを立ち上げ、HelloWorld.tsx の中身を Hello, World!!
に変えてみて保存した時、ブラウザの表示内容も書き変わっていれば成功
まとめ
今回は、Ruby on Rails + Vite + React + TypeScript という構成での開発環境の構築方法を解説しました。この組み合わせには以下のような利点があります。
- Rails の堅牢なバックエンド開発基盤
- Vite による爆速な開発体験とHMR対応
- React + TypeScript での型安全な開発
- Foreman による開発サーバーの統合管理
特に Vite の導入によって、従来の Webpackベースの環境と比較して大幅に開発効率が向上します。ホットリロードの速度も圧倒的に速く、ストレスのない開発が可能になりました。
また、ESLint や TypeScript の設定ファイルもモダンな形式で用意することで、2025年時点での最新のベストプラクティスに則った開発環境を実現できています。
この記事で紹介した手順に従えば、誰でも迷うことなく環境構築を完了させることができるはずです。みなさんも、ぜひモダンな開発環境で快適な Rails 開発を始めてみてください!
P.S.
当初は これに加え + shadcn/ui を導入して AIネイティブな開発環境を作るまでを記事にしたかったのですが、結構なボリュームになりそうだったのでまずは一旦ここまでで区切りとさせてもらいました。
需要があれば shadcn/ui の導入も書いていきます。
参考リンク
株式会社CodeKnightsは
「生成AI時代の、変化の激しい環境を勝ち残るためのWebサービス開発」
を専門として活動しています。
お仕事のお問い合わせはこちらから↓
その他の人気記事
SNSアカウント
X(Twitter)
note
おまけ
導入文のプロンプト:
あなたはWeb開発のエキスパートであり、エバンジェリストです。
いま、「【2025年最新版】Ruby on Rails + Vite + React + TypeScript の開発環境を作る方法 」という記事を書いています。
本文を入力に渡します。
この技術記事の導入文を書いてください。
(お堅すぎるのが飛んできたので)
もう少し口語調で
(採用)
この記事のまとめを書いてください。
Discussion