mcpField
I
inertia-rails-forms

inertia-rails-forms

github.com/inertia-rails開発70488 回インストール37 週間前に更新
リポジトリ

inertia-rails-forms について

Inertia.js と Rails のフォーム開発を、React、Vue、Svelte 向けに案内するスキルです。Form コンポーネントと useForm の使い分けに加え、検証、ファイルアップロード、よくある問題を説明します。

主な機能

  1. 01Form と useForm の使い分け
  2. 02Rails の検証エラー処理
  3. 03ファイルアップロードと進捗表示
  4. 04フレームワーク別のガイド

活用シーン

  1. 01シンプルな作成・編集フォームの構築
  2. 02複数ステップや動的フォームの実装
  3. 03進捗付きファイルアップロードの追加
  4. 04フォーム状態や送信問題の調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/inertia-rails/skills --skill inertia-rails-forms

インストール後、Claude Code で/inertia-rails-formsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
advanced-forms.md
file-uploads.md
svelte.md
vue.md

詳細

Inertia Rails フォーム

Inertia.js + Rails のフルスタックフォーム処理です。

フォームを作成する前に確認してください:

  • シンプルな作成/編集ですか? → <Form> コンポーネント(状態管理は不要です)
  • フィールドごとの UI 要素が必要ですか? → それでも <Form> を使います。 React の useState による UI 状態(プレビュー URL、ファイルサイズ表示など)はフォームデータとは独立しています。 <Form> が送信を処理し、useState が UI を処理します。
  • 複数ステップのウィザード、動的フィールド(入力欄の追加/削除)、または兄弟コンポーネントと共有するフォームデータ(ライブプレビューパネルなど)が必要ですか? → useForm フック
  • react-hook-form を使いたくなりましたか? → 使わないでください。Inertia の <Form> は、CSRF トークン、リダイレクトの追従、Rails からのエラーマッピング、処理状態、ファイルアップロードの検出、履歴状態をすでに処理します。react-hook-form を使うと、これらの処理が重複するか、競合します。

<Form> または useForm を使わないケース:

  • データの検索 — フォーム送信ではありません。デバウンス + preserveState を指定した router.get、または大規模なデータセットには生の fetch を使います。
  • ナビゲーションを伴わない、単一フィールドのインライン編集 — router.patch を直接使うか、そのフィールドにエラーを表示する必要がある場合は useForm を使います。

絶対にしないでください:

  • react-hook-form、vee-validate、sveltekit-superforms を使う — Inertia の <Form> は、CSRF、リダイレクトの追従、エラーマッピング、処理状態、ファイルの検出をすでに処理します。これらのライブラリは Inertia のリクエストライフサイクルと競合します。
  • <Form> に data={...} を渡す — data プロパティはありません。データは入力欄の name 属性から自動的に取得されます。data は useForm の概念です。
  • シンプルな作成/編集に useForm を使う — <Form> は状態管理なしでこれらを処理します。useForm は、複数ステップのウィザード、動的な入力欄の追加/削除、または兄弟コンポーネントと共有するフォームデータに使用してください。
  • 入力欄で defaultValue の代わりに制御された value= を使う — 制御された入力欄は <Form> の変更追跡を迂回するため、isDirty が常に false になります。
  • チェックボックスの value="1" を省略する — 省略すると、ブラウザーは "on" を送信し、Rails はブール値として正しくキャストできません。
  • ループや条件分岐の中で useForm を呼び出す — これはフックです(React のルールが適用されます)。論理上のフォームごとにフォームインスタンスを 1 つ作成してください。

<Form> コンポーネント(推奨)

フォームを処理する最もシンプルな方法です。入力欄の name 属性からデータを自動的に収集するため、手動で状態を管理する必要はありません。<Form> に data プロパティはありません。 data={...} を渡さないでください(これは useForm の概念です)。編集フォームでは、入力欄に defaultValue を使います。

フォームの状態にアクセスするには、レンダー関数の子 {({ errors, processing }) => (...)} を使います。通常の子要素も使えますが、エラー、処理状態、進行状況にはアクセスできません。

import { Form } from '@inertiajs/react'

export default function CreateUser() {
  return (
    <Form method="post" action="/users">
      {({ errors, processing }) => (
        <>
          <input type="text" name="name" />
          {errors.name && <span className="error">{errors.name}</span>}

          <input type="email" name="email" />
          {errors.email && <span className="error">{errors.email}</span>}

          <button type="submit" disabled={processing}>
            {processing ? 'Creating...' : 'Create User'}
          </button>
        </>
      )}
    </Form>
  )
}

// Plain children — valid but no access to errors/processing/progress:
// <Form method="post" action="/users">
//   <input name="name" />
//   <button type="submit">Create</button>
// </Form>
削除フォーム
<Form method="delete" action={`/posts/${post.id}`}>
  {({ processing }) => (
    <button type="submit" disabled={processing}>
      {processing ? 'Deleting...' : 'Delete Post'}
    </button>
  )}
</Form>
レンダー関数の主なプロパティ
プロパティ型目的
errorsRecord<string, string>フィールド名をキーとするバリデーションエラー
processingbooleanリクエストの処理中は true
progress{ percentage: number } | nullアップロードの進行状況(ファイルアップロードのみ)
hasErrorsbooleanエラーが存在する場合は true
wasSuccessfulboolean直近の送信が成功した後は true
recentlySuccessfulboolean成功後 2 秒間 true — 「保存しました!」のフィードバックに最適
isDirtybooleanいずれかの入力欄が初期値から変更されている場合は true
reset(...fields) => void指定したフィールドまたはすべてのフィールドをリセット
clearErrors(...fields) => void指定したエラーまたはすべてのエラーをクリア

追加の <Form> プロパティ(errorBag、only、resetOnSuccess、onBefore、onSuccess、onError、onProgress などのイベントコールバック)は references/advanced-forms.md に記載されています。下記の読み込みトリガーを参照してください。

編集フォーム(値の事前入力)

method="patch" を使い、非制御の初期値を設定します。

  • テキスト/テキストエリア → defaultValue
  • チェックボックス/ラジオボタン → defaultChecked
  • セレクト → <select> の defaultValue

明示的な value のないチェックボックスは "on" を送信します。Rails がブール値として正しくキャストできるよう、value="1" を設定してください。

<Form method="patch" action={`/posts/${post.id}`}>
  {({ errors, processing }) => (
    <>
      <input type="text" name="title" defaultValue={post.title} />
      {errors.title && <span className="error">{errors.title}</span>}

      <label>
        <input type="checkbox" name="published" value="1"
          defaultChecked={post.published} />
        Published
      </label>

      <button type="submit" disabled={processing}>
        {processing ? 'Saving...' : 'Update Post'}
      </button>
    </>
  )}
</Form>
データの変換

useForm を使わずに送信前のデータを整形するには、transform prop を使用します。

useForm での高度な transform については、references/advanced-forms.md を参照してください。

formRef による外部からのアクセス

ref には、レンダー関数の props(FormComponentSlotProps)と同じメソッドと状態が公開されます。 <Form> の外部からフォームを操作する必要がある場合に使用します。 主な ref メソッド:submit()、reset()、clearErrors()、setError()、 getData()、getFormData()、validate()、touch()、defaults()。 状態:errors、processing、progress、isDirty、hasErrors、 wasSuccessful、recentlySuccessful。

import {useRef} from 'react'
import {Form} from '@inertiajs/react'
import type {FormComponentRef} from '@inertiajs/core'

export default function CreateUser() {
  const formRef = useRef<FormComponentRef>(null)

  return (
    <>
      <Form ref={formRef} method='post' action='/users'>
        {({errors}) => (
          <>
            <input type='text' name='name'/>
            {errors.name && <span className='error'>{errors.name}</span>}
          </>
        )}
      </Form>
      <button onClick={() => formRef.current?.submit()}>Submit</button>
      <button onClick={() => formRef.current?.reset()}>Reset</button>
    </>
  )
}

useForm フック

useForm は、複数ステップのウィザード、動的なフィールドの追加/削除、または兄弟コンポーネント間でフォームデータを共有する場合にのみ使用してください。

必須 — ファイル全体を読むこと:useForm フック、transform、 errorBag、resetOnSuccess、複数ステップのフォーム、または setError を使ったクライアント側のバリデーションを使用する場合: references/advanced-forms.md(約330行)— useForm API 全体、transform の例、エラーバッグのスコープ設定、複数ステップのウィザードのパターン、クライアント側のバリデーションが記載されています。

シンプルな作成/編集フォームで <Form> コンポーネントを使用する場合は、advanced-forms.md を読み込まないでください。上記の例で十分です。

ファイルのアップロード

<Form> と useForm はどちらもファイルを自動検出し、FormData に切り替えます。 アップロードの進行状況はレンダー関数に組み込まれています。errors と processing とともに progress を分割代入してください。

type Props = { user: User }

export default function EditProfile({ user }: Props) {
  return (
    <Form method="patch" action="/profile">
      {({ errors, processing, progress }) => (
        <>
          <input type="text" name="name" defaultValue={user.name} />
          {errors.name && <span className="error">{errors.name}</span>}

          <input type="file" name="avatar" />
          {errors.avatar && <span className="error">{errors.avatar}</span>}

          {progress && (
            <progress value={progress.percentage ?? 0} max="100" />
          )}

          <button type="submit" disabled={processing}>
            {processing ? 'Uploading...' : 'Save'}
          </button>
        </>
      )}
    </Form>
  )
}

アップロードに <Form> と useForm のどちらを使うか:

  • ファイルを他のフィールドと一緒に送信する場合(アバター+名前、保存ボタンは1つ)→ <Form> 内にファイル入力を配置
  • 単独の即時アップロードの場合(選択時にアップロードし、保存ボタンはない)→ <Form> と、変更時に呼び出す formRef.submit()
  • ドラッグ&ドロップでアップロードする場合→ useForm(ドロップしたファイルは DOM 入力に含まれないため、setData のほうが簡潔です)

プレビュー/バリデーションには、どちらの方法でも useState を併用してください。詳しくは references/file-uploads.md を参照してください。

Vue/Svelte

上記の例はすべて React の構文を使用しています。Vue 3 または Svelte での同等の方法については、次を参照してください。

  • Vue 3:references/vue.md — #default のスコープ付きスロットを使った <Form>、リアクティブなプロキシを返す useForm(setData ではなく form.email)、v-model によるバインディング
  • Svelte:references/svelte.md — {#snippet} 構文を使った <Form>、Writable ストアを返す useForm($form.email)、bind:value、メソッドのみを公開しリアクティブな状態は公開しない ref

プロジェクトで Vue または Svelte を使用している場合は、必須 — 該当するファイルを読むこと。

トラブルシューティング

症状原因対処法
errors/processing にアクセスできないレンダー関数ではなく通常の子要素を使用している<Form> の子要素は {({ errors, processing }) => (...)} にしてください
フォームが POST ではなく GET を送信するmethod prop がないmethod="post"(または "patch"、"delete")を追加してください
ファイルのアップロードで空の本文が送信されるファイルを含む PUT/PATCHマルチパートの制限 — Inertia は POST に変換するため、_method フィールドを自動追加します
フィールドを修正してもエラーが消えない古いエラー状態エラーは次回の送信時に自動で消去されます。すぐに消去するには clearErrors('field') を使用してください
isDirty が常に falsedefaultValue ではなく value を使用している制御入力(value=)は変更追跡を回避します。defaultValue を使用してください
progress が常に nullフォームにファイル入力がない進行状況の追跡は、<Form> がファイル入力を検出した場合にのみ有効になります
チェックボックスが "on" を送信する明示的な value がないチェックボックスの入力に value="1" を追加してください
開発時にフォームが2回送信されるReact StrictMode による二重呼び出し開発環境では正常です。StrictMode はコンポーネントを再マウントします。本番環境では1回だけ実行されます
プレビュー付きのファイルアップロードに useForm を使用したonChange と useState を「プログラムによるデータ操作」と誤解しているプレビュー UI には <Form> と useState を使用してください。useForm が必要なのは、フォームの送信データを React の状態として保持する必要がある場合(複数ステップ、動的なフィールドの追加/削除)に限られます。ファイルのプレビューはローカル UI の状態であり、フォームデータではありません

関連するスキル

  • サーバー側の PRG とエラー → inertia-rails-controllers(redirect_back、to_hash、flash)
  • shadcn の入力欄 → shadcn-inertia(Input/Select の適応、トースト UI)
  • ページプロパティの型指定 → inertia-rails-typescript(interface ではなく type Props、TS2344)

必須 — ファイル全体を読むこと:画像プレビュー付きのファイルアップロード、Active Storage、またはダイレクトアップロードを扱う場合: references/file-uploads.md(約 200 行) — <Form> を使用した画像プレビュー、Active Storage の統合、ダイレクトアップロードの設定、複数ファイル、進行状況の追跡。

コメント