mcpField
I
inertia-rails-ssr

inertia-rails-ssr

github.com/cole-robertson開発3878 回インストール30 週間前に更新
リポジトリ

inertia-rails-ssr について

Vite Ruby と Node.js を使って Inertia Rails のページをサーバーサイドレンダリングするための設定ガイドです。Vite プラグインと手動設定、本番デプロイ、トラブルシューティングを扱います。

主な機能

  1. 01Vite プラグインまたは手動での SSR 設定
  2. 02React、Vue 3、Svelte のエントリーポイント設定
  3. 03開発・本番環境での SSR のビルドと実行
  4. 04キャッシュ、クラスタリング、条件付きレンダリングの設定
  5. 05タイトルとメタタグの管理
  6. 06ハイドレーションやサーバーエラーの調査

活用シーン

  1. 01Rails の公開ページをサーバーでレンダリング
  2. 02SEO や SNS 共有用のメタデータを準備
  3. 03SSR プロセスのデプロイと管理
  4. 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 サーバーが応答しない
  1. SSR サーバーが実行中か確認します:curl http://localhost:13714
  2. ログを確認します:journalctl -u inertia-ssr -f
  3. ポートが Rails の設定と一致していることを確認します
  4. 開発時に Vite プラグインを使用している場合は、Vite 開発サーバーが実行中であることを確認します
ハイドレーション不一致エラー
  1. クライアントとサーバーで同じコンテンツがレンダリングされることを確認します
  2. 初回レンダリングではブラウザー専用の API を避けます(onMounted/useEffect を使用します)
  3. 日付と時刻のフォーマットの違いを確認します
// 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()
})
メモリリーク
  1. SSR でグローバル状態を変更しないようにします
  2. リクエストスコープの状態のみを使用します
  3. 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

パフォーマンスのヒント

  1. SSR を軽量に保つ - 負荷の高い計算はクライアントに任せます
  2. キャッシュを使用する - 静的または準静的なページでは ssr_cache を有効にします
  3. クラスタリングを使用する - 本番環境ではマルチワーカーモードを有効にします
  4. レイテンシを監視する - SSR のレンダリング時間を追跡します
  5. バンドルを検出する - 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

コメント