laravel-inertia-vue について
laravel-inertia-vueは、Laravel、Inertia.js、Vue 3を使うモノリシックアプリ開発向けのスキルです。優先度別のルールで、ページ、フォーム、ナビゲーション、共有データ、レイアウト、アップロード、応用パターンを扱います。
主な機能
- 016カテゴリにわたる24ルール
- 02型付きInertiaページコンポーネント
- 03フォームとバリデーションのパターン
- 04ナビゲーションと共有データの指針
- 05永続・ネストレイアウト
- 06アップロードと応用ページパターン
活用シーン
- 01型付きInertiaページコンポーネントの作成
- 02フォーム送信とバリデーションの処理
- 03認証情報とフラッシュデータの共有
- 04アップロード、ポーリング、無限スクロールの実装
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ jasim-k/ laravel-agent-skills --skill laravel-inertia-vueインストール後、Claude Code で/laravel-inertia-vueを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
AGENTS.mdmetadata.jsonREADME.mdrules/_sections.md_template.mdform-dirty-tracking.mdform-file-uploads.mdform-progress-indicator.mdform-reset-handling.mdform-transform-data.mdform-useform-basic.mdform-useform-hook.mdform-validation-errors.mdlayout-persistent.mdnav-external-links.mdnav-link-component.mdnav-preserve-state.mdnav-programmatic.mdnav-replace-history.mdpage-component-structure.mdpage-default-layout.mdpage-head-management.mdpage-partial-reloads.mdpage-props-typing.mdpage-scroll-preservation.mdshared-app-config.mdshared-auth-user.mdshared-flash-messages.mdshared-ziggy-routes.md詳細
Laravel + Inertia.js + Vue 3
Laravel、Inertia.js、Vue 3 を使用したモダンなモノリシックアプリケーション構築のための包括的なパターンです。シームレスなフルスタック開発のための 30 以上のルールを収録しています。
適用するタイミング
以下の場合に、これらのガイドラインを参照してください。
- Vue 3 で Inertia のページコンポーネントを作成するとき
- useForm コンポーザブルでフォームを扱うとき
- 共有データと認証を管理するとき
- 永続レイアウトを実装するとき
- ページ間を移動するとき
優先度別ルールカテゴリー
| 優先度 | カテゴリー | 影響度 | プレフィックス |
|---|---|---|---|
| 1 | ページコンポーネント | 重大 | page- |
| 2 | フォームとバリデーション | 重大 | form- |
| 3 | ナビゲーションとリンク | 高 | nav- |
| 4 | 共有データ | 高 | shared- |
| 5 | レイアウト | 中 | layout- |
| 6 | ファイルアップロード | 中 | upload- |
| 7 | 高度なパターン | 低 | advanced- |
クイックリファレンス
1. ページコンポーネント(重大)
page-props-typing- Laravel からのページプロパティの型指定page-component-structure- 標準的なページコンポーネントのパターンpage-head-management- Head によるタイトルとメタタグの管理page-default-layout- ページへのレイアウトの割り当て
2. フォームとバリデーション(重大)
form-useform-basic- useForm の基本的な使い方form-validation-errors- Laravel のバリデーションエラーの表示form-processing-state- フォーム送信状態の処理form-reset-preserve- フォームデータのリセットと保持form-transform- 送信前のデータ変換
3. ナビゲーションとリンク(高)
nav-link-component- ナビゲーションに Link を使用nav-preserve-state- スクロール位置と状態の保持nav-partial-reloads- 変更があった部分だけを再読み込みnav-replace-history- 履歴の置き換えと追加
4. 共有データ(高)
shared-auth-user- 認証済みユーザーへのアクセスshared-flash-messages- フラッシュメッセージの処理shared-global-props- グローバルプロパティへのアクセスshared-typescript- 共有データの型指定
5. レイアウト(中)
layout-persistent- 永続レイアウトのパターンlayout-nested- ネストされたレイアウトlayout-default- デフォルトレイアウトの割り当てlayout-conditional- 条件付きレイアウト
6. ファイルアップロード(中)
upload-basic- ファイルアップロードの基本upload-progress- アップロード進行状況の追跡upload-multiple- 複数ファイルのアップロード
7. 高度なパターン(低)
advanced-polling- リアルタイムポーリングadvanced-prefetch- ページのプリフェッチadvanced-modal-pages- ページとしてのモーダルadvanced-infinite-scroll- 無限スクロール
基本パターン
TypeScript を使用したページコンポーネント
<script setup lang="ts">
import { Head, Link } from '@inertiajs/vue3'
interface Post {
id: number
title: string
excerpt: string
created_at: string
author: {
id: number
name: string
}
}
interface Props {
posts: {
data: Post[]
links: { url: string | null; label: string; active: boolean }[]
}
filters: {
search?: string
}
}
const props = defineProps<Props>()
</script>
<template>
<Head title="Posts" />
<div class="container mx-auto py-8">
<h1 class="text-2xl font-bold mb-6">Posts</h1>
<div class="space-y-4">
<article
v-for="post in props.posts.data"
:key="post.id"
class="p-4 bg-white rounded-lg shadow"
>
<Link :href="route('posts.show', post.id)">
<h2 class="text-xl font-semibold hover:text-blue-600">
{{ post.title }}
</h2>
</Link>
<p class="text-gray-600 mt-2">{{ post.excerpt }}</p>
<p class="text-sm text-gray-400 mt-2">By {{ post.author.name }}</p>
</article>
</div>
</div>
</template>
useForm を使用したフォーム
<script setup lang="ts">
import { Head, Link, useForm } from '@inertiajs/vue3'
interface Category {
id: number
name: string
}
interface Props {
categories: Category[]
}
const props = defineProps<Props>()
const form = useForm({
title: '',
body: '',
category_id: '',
})
function submit() {
form.post(route('posts.store'), {
onSuccess: () => form.reset(),
})
}
</script>
<template>
<Head title="Create Post" />
<form @submit.prevent="submit" class="max-w-2xl mx-auto py-8">
<div class="mb-4">
<label for="title" class="block font-medium mb-1">Title</label>
<input
id="title"
v-model="form.title"
type="text"
class="w-full border rounded px-3 py-2"
/>
<p v-if="form.errors.title" class="text-red-500 text-sm mt-1">
{{ form.errors.title }}
</p>
</div>
<div class="mb-4">
<label for="category" class="block font-medium mb-1">Category</label>
<select
id="category"
v-model="form.category_id"
class="w-full border rounded px-3 py-2"
>
<option value="">Select a category</option>
<option v-for="cat in props.categories" :key="cat.id" :value="cat.id">
{{ cat.name }}
</option>
</select>
<p v-if="form.errors.category_id" class="text-red-500 text-sm mt-1">
{{ form.errors.category_id }}
</p>
</div>
<div class="mb-4">
<label for="body" class="block font-medium mb-1">Content</label>
<textarea
id="body"
v-model="form.body"
rows="10"
class="w-full border rounded px-3 py-2"
/>
<p v-if="form.errors.body" class="text-red-500 text-sm mt-1">
{{ form.errors.body }}
</p>
</div>
<div class="flex gap-4">
<button
type="submit"
:disabled="form.processing"
class="px-4 py-2 bg-blue-600 text-white rounded disabled:opacity-50"
>
{{ form.processing ? 'Creating...' : 'Create Post' }}
</button>
<Link :href="route('posts.index')" class="px-4 py-2 border rounded">
Cancel
</Link>
</div>
</form>
</template>
永続レイアウト
<script setup lang="ts">
import { Link, usePage } from '@inertiajs/vue3'
import { computed } from 'vue'
const page = usePage()
const auth = computed(() => page.props.auth as { user: { name: string } })
</script>
<template>
<div class="min-h-screen bg-gray-100">
<nav class="bg-white shadow">
<div class="container mx-auto px-4 py-3 flex justify-between">
<Link href="/" class="font-bold">My App</Link>
<span>Welcome, {{ auth.user.name }}</span>
</div>
</nav>
<main class="container mx-auto px-4 py-8">
<slot />
</main>
</div>
</template>
<script setup lang="ts">
import AppLayout from '@/Layouts/AppLayout.vue'
defineOptions({ layout: AppLayout })
</script>
<template>
<h1>Dashboard</h1>
</template>
Laravel コントローラー
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StorePostRequest;
use App\Models\Post;
use App\Models\Category;
use Illuminate\Http\RedirectResponse;
use Inertia\Inertia;
use Inertia\Response;
class PostController extends Controller
{
public function index(): Response
{
return Inertia::render('Posts/Index', [
'posts' => Post::with('author:id,name')
->latest()
->paginate(10),
'filters' => request()->only('search'),
]);
}
public function create(): Response
{
return Inertia::render('Posts/Create', [
'categories' => Category::all(['id', 'name']),
]);
}
public function store(StorePostRequest $request): RedirectResponse
{
$post = Post::create([
...$request->validated(),
'user_id' => auth()->id(),
]);
return redirect()
->route('posts.show', $post)
->with('success', 'Post created successfully.');
}
public function show(Post $post): Response
{
return Inertia::render('Posts/Show', [
'post' => $post->load('author', 'category'),
]);
}
}
共有データ(HandleInertiaRequests)
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Inertia\Middleware;
class HandleInertiaRequests extends Middleware
{
public function share(Request $request): array
{
return array_merge(parent::share($request), [
'auth' => [
'user' => $request->user() ? [
'id' => $request->user()->id,
'name' => $request->user()->name,
'email' => $request->user()->email,
] : null,
],
'flash' => [
'success' => $request->session()->get('success'),
'error' => $request->session()->get('error'),
],
]);
}
}
フラッシュメッセージコンポーネント
<script setup lang="ts">
import { usePage } from '@inertiajs/vue3'
import { computed, ref, watch } from 'vue'
const page = usePage()
const flash = computed(() => page.props.flash as { success?: string; error?: string })
const visible = ref(false)
let timer: ReturnType<typeof setTimeout> | null = null
watch(flash, (newFlash) => {
if (newFlash.success || newFlash.error) {
visible.value = true
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
visible.value = false
}, 3000)
}
}, { deep: true })
</script>
<template>
<div v-if="visible" class="fixed top-4 right-4 z-50">
<div
v-if="flash.success"
class="bg-green-500 text-white px-4 py-2 rounded shadow"
>
{{ flash.success }}
</div>
<div
v-if="flash.error"
class="bg-red-500 text-white px-4 py-2 rounded shadow"
>
{{ flash.error }}
</div>
</div>
</template>
使い方
詳しい説明とコード例については、個別のルールファイルを参照してください。
rules/form-useform-basic.md
rules/page-props-typing.md
rules/layout-persistent.md
プロジェクト構成
laravel-inertia-vue/
├── SKILL.md # This file - overview and examples
├── README.md # Quick reference guide
├── AGENTS.md # Integration guide for AI agents
├── metadata.json # Skill metadata and references
└── rules/
├── _sections.md # Rule categories and priorities
├── _template.md # Template for new rules
├── page-*.md # Page component patterns (6 rules)
├── form-*.md # Form handling patterns (8 rules)
├── nav-*.md # Navigation patterns (5 rules)
├── shared-*.md # Shared data patterns (4 rules)
└── layout-*.md # Layout patterns (1 rule)
参考資料
- Inertia.js ドキュメント - Inertia.js 公式ドキュメント
- Laravel ドキュメント - Laravel フレームワークのドキュメント
- Vue 3 ドキュメント - Vue 3 公式ドキュメント
- Ziggy - JavaScript 用 Laravel ルートヘルパー
ライセンス
MIT ライセンス。このスキルは、教育および開発目的で現状のまま提供されます。
メタデータ
- バージョン:1.0.0
- 最終更新日:2026-03-17
- メンテナー:Jasim K
- ルール数:6 カテゴリーにわたる 24 のルール
- 技術スタック:Laravel 12+、Inertia.js 2.0+、Vue 3.3+、TypeScript 5+
コメント
Markdown 対応