inertia-rails-ssr について
Vite Ruby と Node.js を使って Inertia Rails のページをサーバーサイドレンダリングするための設定ガイドです。Vite プラグインと手動設定、本番デプロイ、トラブルシューティングを扱います。
主な機能
- 01Vite プラグインまたは手動での SSR 設定
- 02React、Vue 3、Svelte のエントリーポイント設定
- 03開発・本番環境での SSR のビルドと実行
- 04キャッシュ、クラスタリング、条件付きレンダリングの設定
- 05タイトルとメタタグの管理
- 06ハイドレーションやサーバーエラーの調査
活用シーン
- 01Rails の公開ページをサーバーでレンダリング
- 02SEO や SNS 共有用のメタデータを準備
- 03SSR プロセスのデプロイと管理
- 04ハイドレーション不一致や SSR 障害の診断
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ cole-robertson/ inertia-rails-skills --skill inertia-rails-ssrインストール後、Claude Code で/inertia-rails-ssrを入力して読み込まれたか確認します。
詳細
Inertia Rails サーバーサイドレンダリング(SSR)
サーバーサイドレンダリングでは、JavaScript のページをサーバー上で事前にレンダリングし、完全にレンダリングされた HTML を訪問者に配信します。これにより SEO が向上し、ページの初回読み込みが速くなり、JavaScript が無効でも基本的なナビゲーションが可能になります。
SSR を使用するタイミング
- SEO が重要なページ - ランディングページ、ブログ記事、製品ページ
- ソーシャル共有 - 適切な Open Graph メタタグが必要なページ
- 低速な接続 - JavaScript の読み込み前にユーザーがコンテンツを確認できます
- アクセシビリティ - JavaScript がなくても基本機能を利用できます
要件
- サーバーで Node.js を利用できること
- ビルド設定に Vite Ruby を使用すること
@inertiajs/viteプラグイン(v3 で推奨)
推奨:Vite プラグインのセットアップ(v3)
@inertiajs/vite プラグインを使うと、SSR の設定が大幅に簡単になります。開発時は Vite 開発サーバーを通じて SSR が自動的に実行されるため、別の Node.js プロセスは必要ありません。
1. Vite プラグインをインストールする
npm install @inertiajs/vite
2. Vite を設定する
vite.config.js を更新します。
React:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import inertia from '@inertiajs/vite'
export default defineConfig({
plugins: [
inertia(),
react(),
],
})
Vue 3:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import inertia from '@inertiajs/vite'
export default defineConfig({
plugins: [
inertia(),
vue(),
],
})
Svelte:
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import inertia from '@inertiajs/vite'
export default defineConfig({
plugins: [
inertia(),
svelte(),
],
})
3. エントリーポイントを更新してハイドレーションを行う
SSR が有効な場合、クライアントのエントリーポイントでは完全なレンダリングの代わりにハイドレーションを使用します。
React:
import { createInertiaApp } from '@inertiajs/react'
import { hydrateRoot } from 'react-dom/client'
createInertiaApp({
resolve: (name) => {
const pages = import.meta.glob('../pages/**/*.tsx', { eager: true })
return pages[`../pages/${name}.tsx`]
},
setup({ el, App, props }) {
hydrateRoot(el, <App {...props} />)
},
})
Vue 3:
import { createInertiaApp } from '@inertiajs/vue3'
import { createSSRApp, h } from 'vue'
createInertiaApp({
resolve: (name) => {
const pages = import.meta.glob('../pages/**/*.vue', { eager: true })
return pages[`../pages/${name}.vue`]
},
setup({ el, App, props, plugin }) {
createSSRApp({
render: () => h(App, props),
})
.use(plugin)
.mount(el)
},
})
4. Rails で SSR を有効にする
# config/initializers/inertia_rails.rb
InertiaRails.configure do |config|
config.ssr_enabled = true
# In development, SSR is handled by the Vite dev server automatically.
# Set ssr_url to nil to auto-detect from Vite.
config.ssr_url = nil
# Optional: only enable SSR when the bundle exists (production)
# config.ssr_bundle = Rails.root.join('public/vite-ssr/ssr.js')
# Optional: cache SSR responses
# config.ssr_cache = true
# config.ssr_cache = { expires_in: 1.hour }
# Optional: handle SSR errors gracefully
config.ssr_raise_on_error = !Rails.env.production?
config.on_ssr_error = ->(error, page) {
Rails.logger.error "SSR error for #{page[:component]}: #{error.message}"
}
end
5. 開発を開始する
# Terminal 1: Rails server
bin/rails server
# Terminal 2: Vite dev server (handles SSR automatically)
bin/vite dev
これで完了です。開発時に別の SSR サーバープロセスを起動する必要はありません。
手動での SSR 設定(Vite プラグインを使用しない場合)
Vite プラグインを使用できない場合は、別のエントリーポイントを使って手動で SSR を設定できます。
1. SSR エントリーポイントを作成する
app/frontend/ssr/ssr.js を作成します。
Vue 3:
import { createInertiaApp } from '@inertiajs/vue3'
import createServer from '@inertiajs/vue3/server'
import { renderToString } from '@vue/server-renderer'
import { createSSRApp, h } from 'vue'
const pages = import.meta.glob('../pages/**/*.vue', { eager: true })
createServer((page) =>
createInertiaApp({
page,
render: renderToString,
resolve: (name) => {
const page = pages[`../pages/${name}.vue`]
if (!page) throw new Error(`Page not found: ${name}`)
return page
},
setup({ App, props, plugin }) {
return createSSRApp({
render: () => h(App, props),
}).use(plugin)
},
})
)
React:
import { createInertiaApp } from '@inertiajs/react'
import createServer from '@inertiajs/react/server'
import ReactDOMServer from 'react-dom/server'
const pages = import.meta.glob('../pages/**/*.jsx', { eager: true })
createServer((page) =>
createInertiaApp({
page,
render: ReactDOMServer.renderToString,
resolve: (name) => {
const page = pages[`../pages/${name}.jsx`]
if (!page) throw new Error(`Page not found: ${name}`)
return page
},
setup: ({ App, props }) => <App {...props} />,
})
)
2. Vite Ruby を設定する
config/vite.json を更新します。
{
"all": {
"sourceCodeDir": "app/frontend",
"entrypointsDir": "entrypoints"
},
"development": {
"autoBuild": true
},
"production": {
"ssrBuildEnabled": true
}
}
3. Rails で SSR を有効にする
InertiaRails.configure do |config|
config.ssr_enabled = ViteRuby.config.ssr_build_enabled
config.ssr_url = 'http://localhost:13714'
end
4. ビルドして実行する
bin/vite build
bin/vite build --ssr
bin/vite ssr
本番環境へのデプロイ
本番環境では、Vite プラグインを使用している場合でも、SSR バンドルをビルドして Node.js サーバーを実行する必要があります。
本番環境用にビルドする
bin/vite build
bin/vite build --ssr
プロセスマネージャー(systemd)
/etc/systemd/system/inertia-ssr.service を作成します。
[Unit]
Description=Inertia SSR Server
After=network.target
[Service]
Type=simple
User=deploy
WorkingDirectory=/var/www/myapp/current
ExecStart=/usr/bin/node public/vite-ssr/ssr.js
Restart=on-failure
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target
sudo systemctl enable inertia-ssr
sudo systemctl start inertia-ssr
プロセスマネージャー(PM2)
pm2 start public/vite-ssr/ssr.js --name inertia-ssr
pm2 save
Docker
CMD ["sh", "-c", "node public/vite-ssr/ssr.js & bundle exec puma"]
高度な設定
SSR レスポンスのキャッシュ
SSR レスポンスをキャッシュして Node.js の負荷を軽減します。
InertiaRails.configure do |config|
# Simple boolean — uses default Rails cache store
config.ssr_cache = true
# Or with options
config.ssr_cache = { expires_in: 1.hour }
end
SSR バンドルの検出
バンドルが存在する場合にのみ SSR を試みます。
InertiaRails.configure do |config|
config.ssr_bundle = Rails.root.join('public/vite-ssr/ssr.js')
# Can also be an array of paths to check
# config.ssr_bundle = [
# Rails.root.join('public/vite-ssr/ssr.js'),
# Rails.root.join('public/vite-ssr/ssr.mjs'),
# ]
end
クラスタリング
マルチコアシステムでパフォーマンスを向上させます。
// ssr/ssr.js
createServer(
(page) => createInertiaApp({ /* ... */ }),
{ cluster: true }
)
カスタムポート
createServer(
(page) => createInertiaApp({ /* ... */ }),
{ port: 13715 }
)
条件付き SSR
特定のルートでのみ SSR を有効にします。
class ApplicationController < ActionController::Base
# Disable SSR for admin pages
inertia_config(ssr_enabled: false)
end
class PagesController < ApplicationController
# Enable SSR for public pages
inertia_config(ssr_enabled: Rails.env.production?)
end
# Per-action with a lambda
class PostsController < ApplicationController
inertia_config(ssr_enabled: ->(request) {
request.path.start_with?('/blog')
})
end
タイトルとメタタグ
サーバー管理のメタタグ(v3)
inertia_meta ヘルパーまたは meta: レンダーオプションを使って、コントローラーからメタタグを設定します。
class PostsController < ApplicationController
def show
post = Post.find(params[:id])
render inertia: {
post: post.as_json(only: [:id, :title, :content])
}, meta: {
title: post.title,
description: post.excerpt,
og_image: post.cover_image_url
}
end
end
Head コンポーネントを使用する
Vue 3:
<script setup>
import { Head } from '@inertiajs/vue3'
defineProps(['post'])
</script>
<template>
<Head>
<title>{{ post.title }}</title>
<meta name="description" :content="post.excerpt" />
<meta property="og:title" :content="post.title" />
<meta property="og:image" :content="post.coverImageUrl" />
</Head>
<article>
<h1>{{ post.title }}</h1>
</article>
</template>
React:
import { Head } from '@inertiajs/react'
export default function Show({ post }) {
return (
<>
<Head>
<title>{post.title}</title>
<meta name="description" content={post.excerpt} />
<meta property="og:title" content={post.title} />
</Head>
<article>
<h1>{post.title}</h1>
</article>
</>
)
}
デフォルトのタイトルテンプレート
createInertiaApp({
title: (title) => title ? `${title} - My App` : 'My App',
// ...
})
トラブルシューティング
SSR サーバーが応答しない
- SSR サーバーが実行中か確認します:
curl http://localhost:13714 - ログを確認します:
journalctl -u inertia-ssr -f - ポートが Rails の設定と一致していることを確認します
- 開発時に Vite プラグインを使用している場合は、Vite 開発サーバーが実行中であることを確認します
ハイドレーション不一致エラー
- クライアントとサーバーで同じコンテンツがレンダリングされることを確認します
- 初回レンダリングではブラウザー専用の API を避けます(
onMounted/useEffectを使用します) - 日付と時刻のフォーマットの違いを確認します
// Bad - different on server vs client
const time = new Date().toLocaleTimeString()
// Good - render after mount
const time = ref(null)
onMounted(() => {
time.value = new Date().toLocaleTimeString()
})
メモリリーク
- SSR でグローバル状態を変更しないようにします
- リクエストスコープの状態のみを使用します
- Node.js のメモリ使用量を監視します
SSR が失敗してもエラーが表示されない
SSR の失敗を捕捉してログに記録するため、エラー処理を設定します。
InertiaRails.configure do |config|
config.ssr_raise_on_error = !Rails.env.production?
config.on_ssr_error = ->(error, page) {
Rails.logger.error "SSR error: #{error.message}"
Sentry.capture_exception(error) if defined?(Sentry)
}
end
パフォーマンスのヒント
- SSR を軽量に保つ - 負荷の高い計算はクライアントに任せます
- キャッシュを使用する - 静的または準静的なページでは
ssr_cacheを有効にします - クラスタリングを使用する - 本番環境ではマルチワーカーモードを有効にします
- レイテンシを監視する - SSR のレンダリング時間を追跡します
- バンドルを検出する -
ssr_bundleを使用して、CSR にスムーズにフォールバックします
# Log slow SSR renders
around_action :track_ssr_time, if: -> { request.inertia? }
def track_ssr_time
start = Time.current
yield
duration = Time.current - start
Rails.logger.info "SSR render: #{duration.round(3)}s" if duration > 0.1
end