inertia-rails-controllers について
RailsコントローラーとInertia.jsを使うエンジニア向けのサーバー側パターンガイドです。ページプロパティ、共有データ、遅延・任意の値、バリデーションエラー、外部リダイレクトを解説します。
主な機能
- 01コントローラーの描画とプロパティ渡しのルール
- 02共有データとフラッシュ設定
- 03遅延・任意・キャッシュプロパティ
- 04バリデーションエラーのフォーム項目への対応
- 05外部リダイレクトと履歴の暗号化
活用シーン
- 01Inertia.jsページへのコントローラーデータの受け渡し
- 02遅いクエリや部分更新用プロパティの処理
- 03バリデーションエラーと入力項目の対応付け
- 04決済またはOAuthサービスへのリダイレクト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ inertia-rails/ skills --skill inertia-rails-controllersインストール後、Claude Code で/inertia-rails-controllersを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/authorization.mdconfiguration.mdprop-types.md詳細
Inertia Rails コントローラー
Inertia のレスポンスを返す Rails コントローラーのサーバーサイドパターンです。
プロパティを追加する前に確認してください:
- すべてのページで必要ですか? → アクションごとのプロパティではなく、ベースコントローラー(
InertiaController)のinertia_share - 計算コストが高いですか? →
InertiaRails.defer— ページをすばやく読み込み、後からデータをストリーミングします - 部分的な再読み込みでのみ必要ですか? →
InertiaRails.optional— 初回読み込みではスキップされます - めったに変わらない参照データですか? →
InertiaRails.once— ページ遷移をまたいでキャッシュされます
絶対にしないでください:
- 外部 URL(Stripe、OAuth、SSO)へのリダイレクトに
redirect_toを使う — 302 を返しますが、Inertia クライアントはレスポンスを JSON として解析しようとするため、リダイレクトが壊れます。inertia_locationを使ってください(409 とX-Inertia-Locationヘッダーを返します)。 - バリデーションエラーに
errors.full_messagesを使う — フィールドキーのない単純な文字列が生成されるため、フロントエンドでエラーを対応する入力フィールドにマッピングできません。errors.to_hash(true)を使ってください。 inertia.defer、Inertia.defer、またはinertia_rails.deferを使う — 正しい構文はInertiaRails.defer { ... }です。すべてのプロパティヘルパーはInertiaRails定数上のモジュールメソッドです。- インスタンス変数が自動的にプロパティとして渡されると想定する — 自動では渡されません(
alba-inertiagem が設定されている場合を除く)。フロントエンドにプロパティを渡すすべてのアクションで、render inertia: { key: data }を呼び出す必要があります。 config.flash_keysを更新せずにsuccess/errorをフラッシュキーとして使う — Rails のデフォルトはnotice/alertです。カスタムキーはイニシャライザーの設定と TypeScript のFlashData型の両方に追加する必要があります。
レンダリング構文
default_render: trueの落とし穴: この設定はコントローラー/アクションからコンポーネント名を自動推測するだけで、インスタンス変数をプロパティとして自動的に渡すわけではありません。default_render: trueを設定したアクションで@posts = Post.allと記述すると、正しいコンポーネントはレンダリングされますが、フロントエンドにデータは一切送られません。 インスタンス変数がプロパティとして自動シリアライズされるのは、alba-inertiagem が設定されている場合のみです。これに依存する前にGemfileを確認してください。この gem がなければ、ページにデータを渡すにはrender inertia: { posts: data }を使う必要があります。空のアクション(
def index; end)が正しいのは、データを必要としないページ(例:静的なダッシュボードページ、ログインフォーム)のみです。アクションがデータベースをクエリする場合は、データとともにrender inertia:を呼び出す必要があります。
| 状況 | 構文 | コンポーネントのパス |
|---|---|---|
| アクションがデータを読み込む | render inertia: { users: data } | コントローラー/アクションから推測 |
| アクションがデータを読み込まない(静的ページ) | 空のアクション、または render inertia: {} | コントローラー/アクションから推測 |
| 別のページをレンダリングする | render inertia: 'errors/show', props: { error: e } | 明示的なパス |
目安: アクションがデータベースにアクセスする場合は、データとともに render inertia: を呼び出す必要があります。
アクション本体が空の場合、ページに渡されるのは共有プロパティ(inertia_share のもの)のみです。
# CORRECT — data passed as props
def index
render inertia: { users: users_data, stats: InertiaRails.defer { ExpensiveQuery.run } }
end
# CORRECT — static page, no data needed
def index; end
# WRONG — @posts is NEVER sent to the frontend (without alba-inertia)
def index
@posts = Post.all
end
注: プロジェクトで
alba-inertiagem を使っている場合(Gemfileを確認してください)、インスタンス変数はプロパティとして自動シリアライズされるため、明示的なrender inertia:は不要です。 この規約についてはalba-inertiaスキルを参照してください。
Prop Types
InertiaRails.defer — inertia.deferでもInertia.deferでもありません。すべてのプロップヘルパーはInertiaRailsのモジュールメソッドです。
| タイプ | 構文 | 動作 |
|---|---|---|
| 通常 | { key: value } | 常に評価され、常に含まれます |
| 遅延 | -> { expensive_value } | 初回ページのレンダリング時に含まれ、部分リロード時には遅延評価されます |
| オプション | InertiaRails.optional { ... } | リクエストで指定された場合にのみ評価されます |
| Defer | InertiaRails.defer { ... } | 初回ページのレンダリング後に読み込まれます |
| Defer(グループ化) | InertiaRails.defer(group: 'name') { ... } | グループ化された遅延データ — 並列で取得されます |
| Once | InertiaRails.once { ... } | 一度だけ解決され、ナビゲーション間で保持されます |
| Merge | InertiaRails.merge { ... } | 既存の配列に追加されます(無限スクロール) |
| Deep merge | InertiaRails.deep_merge { ... } | 既存のオブジェクトにディープマージされます |
| Always | InertiaRails.always { ... } | 部分リロード時も含まれます |
| Scroll | InertiaRails.scroll { ... } | 無限スクロール用のスクロール対応プロップです |
def index
render inertia: {
filters: filter_params,
messages: -> { messages_scope.as_json },
stats: InertiaRails.defer { Dashboard.stats },
chart: InertiaRails.defer(group: 'analytics') { Dashboard.chart },
countries: InertiaRails.once { Country.pluck(:name, :code) },
posts: InertiaRails.merge { @posts.as_json },
csrf: InertiaRails.always { form_authenticity_token },
}
end
遅延プロップ — フルスタックの例
サーバーは時間のかかるデータを遅延させ、クライアントはフォールバックを表示した後、コンテンツに差し替えます。
# Controller
def show
render inertia: {
basic_stats: Stats.quick_summary,
analytics: InertiaRails.defer { Analytics.compute_slow },
}
end
// Page component — child reads deferred prop from page props
import { Deferred, usePage } from '@inertiajs/react'
export default function Dashboard({ basic_stats }: Props) {
return (
<>
<QuickStats data={basic_stats} />
<Deferred data="analytics" fallback={<div>Loading analytics...</div>}>
<AnalyticsPanel />
</Deferred>
</>
)
}
function AnalyticsPanel() {
const { analytics } = usePage<{ analytics: Analytics }>().props
return <div>{analytics.revenue}</div>
}
共有データ
コントローラーではinertia_shareを使用します。コントローラーのコンテキスト(current_user、
リクエスト)が必要です。イニシャライザーで扱うのはconfig.*の設定(version、flash_keys)のみです。
class ApplicationController < ActionController::Base
# Static
inertia_share app_name: 'MyApp'
# Using lambdas (most common)
inertia_share auth: -> { { user: current_user&.as_json(only: [:id, :name, :email, :role]) } }
# Conditional
inertia_share if: :user_signed_in? do
{ notifications: -> { current_user.unread_notifications_count } }
end
end
ラムダ版とアクションスコープ版については、references/configuration.mdを参照してください。
評価順序: 複数のinertia_share呼び出しは上から順にマージされます。子コントローラーが親と同じキーを共有する場合は、子の値が優先されます。ブロックとラムダによる共有はリクエストごとに遅延評価され、Inertia以外のリクエストでは実行されません。
フラッシュメッセージ
フラッシュは自動です。必要に応じて公開するキーを設定してください。
# config/initializers/inertia_rails.rb
InertiaRails.configure do |config|
config.flash_keys = %i[notice alert toast] # default: %i[notice alert]
end
コントローラーでは標準のRailsフラッシュを使用します。
redirect_to users_path, notice: "User created!"
# or
flash.alert = "Something went wrong"
redirect_to users_path
リダイレクトとバリデーションエラー
作成・更新・削除の後は、必ずリダイレクトしてください(Post-Redirect-Get)。標準のRailsのredirect_toが使用できます。Inertia固有の対応が必要なのは、バリデーションエラーの処理です。
def create
@user = User.new(user_params)
if @user.save
redirect_to users_path, notice: "Created!"
else
redirect_back_or_to new_user_path, inertia: { errors: @user.errors.to_hash(true) }
end
end
to_hashとto_hash(true)の違い: to_hashは{ name: ["can't be blank"] }を返し、to_hash(true)は{ name: ["Name can't be blank"] }を返します。キーは入力欄のname属性と一致している必要があります。一致しない場合、エラーは対応する入力欄の横に表示されません。
errors.full_messagesは絶対に使用しないでください — フィールドキーのないフラットな文字列が生成されるため、フロントエンドでエラーを対応する入力欄に紐付けられません。
プロップとしての認可情報
リソースごとのcanハッシュとして権限を渡してください。フロントエンドは表示を制御し、サーバーはアクセスを強制します。inertia-rails-controllersとinertia-rails-pagesのスキルを参照してください。
必須 — 認可プロップを実装する場合はファイル全体を読むこと:
references/authorization.md(約40行) — Action Policy/Pundit/CanCanCanの例を含む、フルスタックのcanパターンです。
フロントエンドに権限データを渡さない場合は読み込まないでください。
外部リダイレクト(inertia_location)
重要: 外部URLへのredirect_toはInertiaを壊します。クライアントは302を受け取りますが、ページ全体のリダイレクトではなく、Inertiaのレスポンス(JSON)として処理しようとします。inertia_locationはX-Inertia-Locationヘッダー付きの409を返し、クライアントにwindow.location = urlを実行させます。
# Stripe checkout — MUST use inertia_location, not redirect_to
def create
checkout_session = Current.user.payment_processor.checkout(
mode: "payment",
line_items: "price_xxx",
success_url: enrollments_url,
cancel_url: course_url(@course),
)
inertia_location checkout_session.url
end
決済プロバイダー、OAuth、外部サービスなど、Inertiaアプリの外部にあるURLにはinertia_locationを使用してください。
履歴の暗号化
ブラウザーの履歴状態に含まれるページデータを暗号化します — config.encrypt_history = Rails.env.production?。
ログアウトやロール変更時にはredirect_to path, inertia: { clear_history: true }を使用してください。
サーバー側とクライアント側の例を含むセットアップ全体については、
references/configuration.mdを参照してください。
設定
InertiaRails.configureのすべてのオプション(version、encrypt_history、flash_keysなど)については、references/configuration.mdを参照してください。
トラブルシューティング
| 症状 | 原因 | 対処方法 |
|---|---|---|
| Stripe/OAuth リダイレクトで 302 ループが発生する | 外部 URL に対して redirect_to を使用している | inertia_location を使用してください。409 と X-Inertia-Location ヘッダーが返されます |
| エラーがフィールドの横に表示されない | エラーキーが入力の name と一致していない | to_hash のキーは入力の name 属性と完全に一致させる必要があります |
TS2305:@/routes に postsPath が見つからない | ルート追加後に js-routes が再生成されていない | config/routes.rb を変更した後、rails js_routes:generate を実行してください |
関連スキル
- フォームエラーの表示 →
inertia-rails-forms - フラッシュトースト UI →
inertia-rails-pages(アクセス)+shadcn-inertia(Sonner) - クライアント側での遅延表示 →
inertia-rails-pages(<Deferred>コンポーネント) - 型安全な props →
inertia-rails-typescriptまたはalba-inertia(シリアライザー) - テスト →
inertia-rails-testing
参考資料
merge、scroll、deep_merge などの高度な prop 型を使用する場合、または複数の prop オプションを組み合わせる場合は、必ずファイル全体を読んでください:
references/prop-types.md(約 180 行) — すべての prop 型の詳細な動作、エッジケース、組み合わせルール。
基本的な defer、optional、once、always の使用では、prop-types.md を読み込まないでください。上の表で十分です。
初めて InertiaRails.configure を設定するとき、または設定に関する問題をデバッグするときに限り、references/configuration.md(約 180 行)を読み込んでください。通常のコントローラー作業では読み込まないでください。