Remix × Vite 構成の作り方:開発サーバ・ビルド・エイリアス設定【完全ガイド】
Remix の開発体験が大きく進化しました。従来の Remix Compiler から、高速で柔軟な Vite へと移行できるようになったのです。 本記事では、Remix × Vite 構成の構築方法を、開発サーバの起動からビルド設定、パスエイリアスの活用まで、実践的な手順とともに解説いたします。
初めて Vite を導入する方でも、ステップバイステップで理解できる内容になっていますので、ぜひ最後までお読みください。
背景
Remix の開発環境の変遷
Remix は当初、独自のコンパイラ(@remix-run/dev)を使用していました。
しかし、フロントエンド開発ツールのエコシステムが進化する中で、より高速で拡張性の高いビルドツールへの需要が高まってきたのです。
そこで登場したのが、Vite との統合です。Vite は次世代のフロントエンドビルドツールとして、以下の特徴を持っています。
Vite が選ばれる理由
Remix チームが正式に Vite をサポートすることを決定した背景には、いくつかの明確な理由があります。
以下の図は、従来の Remix Compiler と Vite 構成の違いを示しています。
mermaidflowchart TB
subgraph old["従来のRemix Compiler"]
oldDev["remix dev"] --> oldBuild["esbuild による<br/>バンドル"]
oldBuild --> oldOutput["ビルド出力"]
end
subgraph new["Vite構成"]
newDev["vite dev"] --> viteHMR["高速HMR<br/>(ミリ秒単位)"]
newBuild["vite build"] --> viteBuild["Rollup最適化<br/>バンドル"]
viteHMR --> newOutput["開発サーバ"]
viteBuild --> prodOutput["本番ビルド"]
end
この図から分かるように、Vite 構成では開発とビルドの両面で最適化されています。
主な利点:
| # | 項目 | 従来の Remix Compiler | Vite 構成 |
|---|---|---|---|
| 1 | 開発サーバ起動速度 | 数秒〜数十秒 | ミリ秒単位 ★★★ |
| 2 | HMR(Hot Module Replacement) | あり | 高速・安定 ★★★ |
| 3 | プラグインエコシステム | 限定的 | 豊富(1000+) ★★★ |
| 4 | ビルド最適化 | esbuild | Rollup(細かい制御可能) ★★ |
| 5 | TypeScript 対応 | 要設定 | ネイティブサポート ★★★ |
課題
移行時に直面する問題
Remix × Vite 構成への移行は魅力的ですが、いくつかの課題に直面することがあります。
1. 設定ファイルの違い
従来のremix.config.jsからvite.config.tsへの移行が必要です。
設定項目の名称や構造が異なるため、既存プロジェクトの移行時には注意が必要でしょう。
2. プラグインの互換性
Remix の機能(ファイルベースルーティング、ローダー、アクションなど)を Vite 上で動作させるには、専用のプラグイン(@remix-run/devの Vite プラグイン)が必要になります。
3. パスエイリアスの設定
開発時に便利な@/componentsのようなパスエイリアスを使うには、Vite と TypeScript の両方で設定が必要です。
片方だけでは正しく動作しないため、設定漏れが発生しやすい問題があります。
4. ビルド出力の違い
Vite のビルド出力構造は、従来の Remix Compiler と異なります。 デプロイ設定や環境変数の読み込み方法も調整が必要でしょう。
以下の図は、パスエイリアスの解決フローを示しています。
mermaidflowchart LR
code["コード内<br/>import from '@/utils'"] --> tsconfig["tsconfig.json<br/>paths設定"]
code --> viteConfig["vite.config.ts<br/>resolve.alias設定"]
tsconfig --> tsc["TypeScript<br/>型チェックOK"]
viteConfig --> viteDev["Vite開発サーバ<br/>モジュール解決OK"]
viteConfig --> viteBuild["Viteビルド<br/>バンドルOK"]
tsc --> success["正常動作"]
viteDev --> success
viteBuild --> success
両方の設定が揃って初めて、パスエイリアスが完全に機能します。
解決策
Remix × Vite 構成の導入手順
ここからは、実際に Remix × Vite 構成を構築する具体的な手順を解説します。 段階的に進めることで、確実にセットアップできるでしょう。
ステップ 1:新規プロジェクトの作成
まず、Vite テンプレートを使って Remix プロジェクトを作成します。
bash# Remixプロジェクトを作成(Viteテンプレート使用)
npx create-remix@latest my-remix-app --template remix-run/remix/templates/vite
bash# プロジェクトディレクトリに移動
cd my-remix-app
bash# 依存関係をインストール
yarn install
これで、Vite 構成の Remix プロジェクトが作成されました。 テンプレートには必要な設定があらかじめ含まれているため、すぐに開発を始められます。
ステップ 2:Vite 設定ファイルの理解
プロジェクトルートに生成されるvite.config.tsが、Vite 構成の中心となります。
このファイルで、開発サーバ、ビルド、プラグインなどを制御します。
基本的な vite.config.ts の構造
typescriptimport { vitePlugin as remix } from '@remix-run/dev';
import { defineConfig } from 'vite';
上記は、必要なモジュールのインポートです。
@remix-run/devから Remix 用の Vite プラグインを読み込んでいます。
typescriptexport default defineConfig({
plugins: [
remix({
// Remix固有の設定
ignoredRouteFiles: ['**/*.css'],
}),
],
});
plugins配列に Remix プラグインを追加することで、Remix の機能が Vite 上で動作します。
ignoredRouteFilesでは、ルートとして認識しないファイルパターンを指定できますね。
ステップ 3:開発サーバの設定
開発体験を向上させるため、開発サーバのカスタマイズを行います。
typescriptexport default defineConfig({
plugins: [remix()],
server: {
port: 3000,
host: true,
open: true,
},
});
設定項目の説明:
| # | 設定項目 | 説明 | デフォルト値 |
|---|---|---|---|
| 1 | port | 開発サーバのポート番号 | 5173 |
| 2 | host | ネットワークからのアクセス許可(true: 0.0.0.0) | localhost |
| 3 | open | サーバ起動時にブラウザを自動で開く | false |
この設定により、http://localhost:3000で開発サーバが起動し、自動でブラウザが開きます。
HMR(Hot Module Replacement)の設定
Vite の HMR は標準で有効ですが、細かい調整も可能です。
typescriptexport default defineConfig({
plugins: [remix()],
server: {
port: 3000,
hmr: {
overlay: true,
protocol: 'ws',
},
},
});
overlay: trueにより、エラーが発生した際にブラウザ上にオーバーレイで表示されます。
開発中のデバッグが格段に楽になるでしょう。
ステップ 4:ビルド設定の最適化
本番環境向けのビルド設定を行います。
typescriptexport default defineConfig({
plugins: [remix()],
build: {
target: 'esnext',
minify: 'esbuild',
sourcemap: false,
rollupOptions: {
output: {
manualChunks: undefined,
},
},
},
});
ビルド設定の各項目:
- target: 出力する JavaScript のバージョン(
esnextで最新仕様を利用) - minify: コード圧縮方法(
esbuildは高速、terserはより小さいサイズ) - sourcemap: ソースマップの生成(本番環境では false を推奨)
- rollupOptions: Rollup の詳細設定
チャンク分割の戦略
大規模アプリケーションでは、適切なチャンク分割が重要です。
typescriptexport default defineConfig({
plugins: [remix()],
build: {
rollupOptions: {
output: {
manualChunks(id) {
// node_modules配下を別チャンクに
if (id.includes('node_modules')) {
return 'vendor';
}
},
},
},
},
});
この設定により、外部ライブラリがvendor.jsとして分離されます。
アプリケーションコードの更新時に、ユーザーはライブラリ部分を再ダウンロードする必要がなくなりますね。
ステップ 5:パスエイリアスの設定
コードの可読性と保守性を高めるため、パスエイリアスを設定します。
../../../components/Buttonのような相対パスを、@/components/Buttonのようにシンプルに書けるようになります。
vite.config.ts でのエイリアス設定
typescriptimport { vitePlugin as remix } from '@remix-run/dev';
import { defineConfig } from 'vite';
import path from 'path';
まず、Node.js のpathモジュールをインポートします。
これを使って、絶対パスを生成します。
typescriptexport default defineConfig({
plugins: [remix()],
resolve: {
alias: {
'@': path.resolve(__dirname, './app'),
'~': path.resolve(__dirname, './app'),
},
},
});
@と~の両方をappディレクトリに紐付けています。
どちらも同じ意味ですが、プロジェクトの慣習に合わせて選べますね。
tsconfig.json でのパス設定
TypeScript の型チェックを正しく機能させるため、tsconfig.jsonにも同じパス設定が必要です。
json{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./app/*"],
"~/*": ["./app/*"]
}
}
}
baseUrlを設定することで、pathsの起点が定まります。
これにより、TypeScript がインポート文を正しく解釈できるようになるでしょう。
より詳細なエイリアス設定
実際のプロジェクトでは、複数のエイリアスを使い分けると便利です。
typescriptexport default defineConfig({
plugins: [remix()],
resolve: {
alias: {
'@': path.resolve(__dirname, './app'),
'@components': path.resolve(
__dirname,
'./app/components'
),
'@utils': path.resolve(__dirname, './app/utils'),
'@hooks': path.resolve(__dirname, './app/hooks'),
'@styles': path.resolve(__dirname, './app/styles'),
'@types': path.resolve(__dirname, './app/types'),
},
},
});
用途別にエイリアスを分けることで、インポート文がより意味的になります。
対応するtsconfig.jsonの設定は以下の通りです。
json{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./app/*"],
"@components/*": ["./app/components/*"],
"@utils/*": ["./app/utils/*"],
"@hooks/*": ["./app/hooks/*"],
"@styles/*": ["./app/styles/*"],
"@types/*": ["./app/types/*"]
}
}
}
これで、import Button from "@components/Button"のように、明確なインポートが可能になりました。
ステップ 6:環境変数の設定
Vite では、環境変数の扱い方が独特です。 正しく設定することで、開発・本番環境での値の切り替えが簡単になります。
.env ファイルの作成
プロジェクトルートに.envファイルを作成します。
bash# .env
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My Remix App
重要な注意点:
Vite では、クライアント側で使用する環境変数にはVITE_プレフィックスが必要です。
このプレフィックスがない変数は、ビルド時にバンドルされません。
環境変数の読み込み方法
Remix コンポーネント内での環境変数の使用例です。
typescriptexport default function Index() {
// Viteの環境変数にアクセス
const apiUrl = import.meta.env.VITE_API_URL;
const appTitle = import.meta.env.VITE_APP_TITLE;
return (
<div>
<h1>{appTitle}</h1>
<p>API URL: {apiUrl}</p>
</div>
);
}
import.meta.env経由で環境変数にアクセスします。
これは、Vite の標準的な方法ですね。
TypeScript の型定義
環境変数に型をつけることで、より安全に使用できます。
typescript// app/types/env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_APP_TITLE: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
この型定義により、環境変数のオートコンプリートと型チェックが有効になります。
具体例
実践的なプロジェクト構成
ここでは、実際のプロジェクトで使用できる完全な設定例を紹介します。 この構成は、中〜大規模の Remix アプリケーションに適しています。
完全な vite.config.ts
以下は、これまで解説した内容を統合した、本番環境対応の設定ファイルです。
typescriptimport { vitePlugin as remix } from '@remix-run/dev';
import { defineConfig } from 'vite';
import path from 'path';
typescriptexport default defineConfig({
// Remixプラグインの設定
plugins: [
remix({
ignoredRouteFiles: ["**/*.css", "**/*.test.{ts,tsx}"],
serverModuleFormat: "esm",
}),
],
serverModuleFormat: "esm"により、サーバー側も ES モジュールとして出力されます。
これは、Node.js の最新機能を活用できるメリットがあります。
typescript // 開発サーバの設定
server: {
port: 3000,
host: true,
open: true,
hmr: {
overlay: true,
},
},
typescript // ビルド設定
build: {
target: "esnext",
minify: "esbuild",
sourcemap: process.env.NODE_ENV === "development",
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes("node_modules")) {
if (id.includes("react") || id.includes("react-dom")) {
return "react-vendor";
}
return "vendor";
}
},
},
},
},
React 関連ライブラリを別チャンクに分離することで、キャッシュ効率が向上します。
typescript // パスエイリアスの設定
resolve: {
alias: {
"@": path.resolve(__dirname, "./app"),
"@components": path.resolve(__dirname, "./app/components"),
"@utils": path.resolve(__dirname, "./app/utils"),
"@hooks": path.resolve(__dirname, "./app/hooks"),
"@styles": path.resolve(__dirname, "./app/styles"),
"@types": path.resolve(__dirname, "./app/types"),
"@lib": path.resolve(__dirname, "./app/lib"),
},
},
typescript // CSS設定
css: {
modules: {
localsConvention: "camelCase",
},
},
});
CSS Modules を使用する場合、localsConventionにより、クラス名のケースを自動変換できます。
完全な tsconfig.json
TypeScript の設定も、Vite と連携させます。
json{
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"types": ["@remix-run/node", "vite/client"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"target": "ES2022",
"strict": true,
"allowJs": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
json "baseUrl": ".",
"paths": {
"@/*": ["./app/*"],
"@components/*": ["./app/components/*"],
"@utils/*": ["./app/utils/*"],
"@hooks/*": ["./app/hooks/*"],
"@styles/*": ["./app/styles/*"],
"@types/*": ["./app/types/*"],
"@lib/*": ["./app/lib/*"]
},
json "noEmit": true
},
"include": ["**/*.ts", "**/*.tsx", "**/.server/**/*.ts", "**/.server/**/*.tsx", "**/.client/**/*.ts", "**/.client/**/*.tsx"]
}
"noEmit": trueにより、TypeScript はトランスパイルせず、型チェックのみ行います。
実際のトランスパイルは Vite が担当しますね。
ディレクトリ構造の例
パスエイリアスを活用した、推奨ディレクトリ構造です。
rubymy-remix-app/
├── app/
│ ├── components/ # @components
│ │ ├── ui/
│ │ │ ├── Button.tsx
│ │ │ └── Input.tsx
│ │ └── layout/
│ │ ├── Header.tsx
│ │ └── Footer.tsx
│ ├── hooks/ # @hooks
│ │ ├── useAuth.ts
│ │ └── useFetch.ts
│ ├── lib/ # @lib
│ │ ├── api.ts
│ │ └── constants.ts
│ ├── routes/
│ │ ├── _index.tsx
│ │ └── about.tsx
│ ├── styles/ # @styles
│ │ ├── global.css
│ │ └── theme.css
│ ├── types/ # @types
│ │ ├── env.d.ts
│ │ └── user.ts
│ └── utils/ # @utils
│ ├── format.ts
│ └── validate.ts
├── public/
├── .env
├── package.json
├── tsconfig.json
└── vite.config.ts
実際の使用例
パスエイリアスを使った実際のコード例を見てみましょう。
コンポーネントでの使用
typescript// app/routes/_index.tsx
import { json } from '@remix-run/node';
import type { LoaderFunctionArgs } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';
// パスエイリアスを使ったインポート
import { Button } from '@components/ui/Button';
import { Header } from '@components/layout/Header';
import { useAuth } from '@hooks/useAuth';
import { formatDate } from '@utils/format';
import type { User } from '@types/user';
相対パスを使わないため、ファイルの移動時にインポート文を修正する必要がありません。
typescriptexport async function loader({
request,
}: LoaderFunctionArgs) {
const apiUrl = process.env.VITE_API_URL;
// データ取得ロジック
return json({ users: [] });
}
typescriptexport default function Index() {
const { users } = useLoaderData<typeof loader>();
const { isAuthenticated } = useAuth();
return (
<div>
<Header />
<main>
<h1>Welcome to Remix with Vite</h1>
{isAuthenticated && (
<Button variant='primary'>Get Started</Button>
)}
</main>
</div>
);
}
ビルドとデプロイ
Vite 構成でのビルドとデプロイの流れを解説します。
package.json のスクリプト設定
json{
"scripts": {
"dev": "remix vite:dev",
"build": "remix vite:build",
"start": "remix-serve ./build/server/index.js",
"typecheck": "tsc"
}
}
スクリプトの説明:
| # | コマンド | 説明 | 用途 |
|---|---|---|---|
| 1 | dev | 開発サーバを起動 | ローカル開発 |
| 2 | build | 本番用ビルドを実行 | デプロイ前 |
| 3 | start | ビルド済みアプリを起動 | 本番環境 |
| 4 | typecheck | TypeScript 型チェック | CI/CD |
ビルドの実行
bash# 本番用ビルド
yarn build
このコマンドにより、build/ディレクトリに最適化されたファイルが生成されます。
以下の図は、Vite ビルドプロセスの全体像です。
mermaidflowchart TB
start["yarn build"] --> analyze["ソースコード<br/>解析"]
analyze --> deps["依存関係<br/>グラフ作成"]
deps --> transform["TypeScript<br/>→ JavaScript変換"]
transform --> bundle["Rollupによる<br/>バンドル"]
bundle --> optimize["コード最適化<br/>・圧縮・Tree-shaking"]
optimize --> chunks["チャンク分割<br/>vendor/react-vendor"]
chunks --> output["build/ディレクトリ<br/>出力"]
output --> done["ビルド完了"]
ビルド出力の確認:
bash# ビルド結果の確認
ls -la build/
bashbuild/
├── client/ # クライアント側のバンドル
│ ├── assets/
│ └── index.html
└── server/ # サーバー側のバンドル
└── index.js
デプロイ例(Vercel)
Vercel へのデプロイは、Remix × Vite 構成でもスムーズです。
bash# Vercel CLIをインストール
yarn global add vercel
bash# デプロイを実行
vercel --prod
Vercel は自動的にvite buildを実行し、最適化されたアプリケーションをデプロイします。
トラブルシューティング
よくある問題と解決方法をまとめました。
Error: Cannot find module '@/components/Button'
エラーコード: Error: Cannot find module '@/components/Button'
エラーメッセージ:
vbnetError: Cannot find module '@/components/Button'
at Module._resolveFilename (internal/modules/cjs/loader.js:xxx)
発生条件: パスエイリアスの設定が不完全な場合に発生します。
解決方法:
vite.config.tsのresolve.aliasを確認tsconfig.jsonのpathsを確認- 両方が一致しているか確認
- 開発サーバを再起動
bash# 開発サーバを再起動
yarn dev
ビルドエラー:チャンク分割の問題
エラーコード: RollupError: "default" is not exported by ...
発生条件: 外部ライブラリのインポート方法が誤っている場合。
解決方法:
typescript// ❌ 誤った書き方
import lib from 'some-library';
// ✅ 正しい書き方
import * as lib from 'some-library';
// または
import { specificFunction } from 'some-library';
まとめ
Remix × Vite 構成は、開発体験を大きく向上させる素晴らしい選択肢です。 本記事で解説した内容を振り返ってみましょう。
重要なポイント
設定面での要点:
- vite.config.ts:開発サーバ、ビルド、プラグインを一元管理
- パスエイリアス:Vite と TypeScript の両方で設定が必須
- 環境変数:
VITE_プレフィックスをクライアント側変数に使用
開発面でのメリット:
- 高速な開発サーバ起動(ミリ秒単位)
- 安定した HMR による快適な開発体験
- 豊富な Vite プラグインエコシステムの活用
ビルド面での利点:
- Rollup による高度な最適化
- 柔軟なチャンク分割戦略
- Tree-shaking によるバンドルサイズ削減
次のステップ
Remix × Vite 構成をマスターしたら、以下の発展的なトピックにも挑戦してみてください。
- Tailwind CSS の統合:Vite プラグインで Tailwind を導入
- PWA 対応:
vite-plugin-pwaでプログレッシブ Web アプリ化 - パフォーマンス計測:Vite-plugin を使った詳細な分析
- E2E テスト:Playwright との連携
Vite の柔軟性と Remix の開発思想が組み合わさることで、モダンな Web アプリケーション開発がより楽しくなります。 ぜひ、実際のプロジェクトで試してみてくださいね。
関連リンク
articleRemix × Vite 構成の作り方:開発サーバ・ビルド・エイリアス設定【完全ガイド】
articleRemix を選ぶ基準:認証・API・CMS 観点での要件適合チェック
articleRemix の仕組みを図解で理解:ルーティング/データロード/アクションの全体像
articleRemix で「Hydration failed」を解決:サーバ/クライアント不整合の診断手順
articleRemix 本番運用チェックリスト:ビルド・監視・バックアップ・脆弱性対応
articleRemix で管理画面テンプレ:表・フィルタ・CSV エクスポートの鉄板構成
articleVite 依存セキュリティ運用:`pnpm audit` / `depcheck` / Trivy で継続監視
articleRemix × Vite 構成の作り方:開発サーバ・ビルド・エイリアス設定【完全ガイド】
articleVite 大規模案件の `vite.config.ts` 設計:環境・役割別に設定を分割する
articleVite プラグインフック対応表:Rollup → Vite マッピング早見表
articleVite で Web Worker / SharedWorker を TypeScript でバンドルする初期設定
articleテスト環境比較:Vitest vs Jest vs Playwright CT ― Vite プロジェクトの最適解
articlegpt-oss 推論パラメータ早見表:temperature・top_p・repetition_penalty...その他まとめ
articleLangChain を使わない判断基準:素の API/関数呼び出しで十分なケースと見極めポイント
articleJotai エコシステム最前線:公式&コミュニティ拡張の地図と選び方
articleGPT-5 監査可能な生成系:プロンプト/ツール実行/出力のトレーサビリティ設計
articleFlutter の描画性能を検証:リスト 1 万件・画像大量・アニメ多用の実測レポート
articleJest が得意/不得意な領域を整理:単体・契約・統合・E2E の住み分け最新指針
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来