Tools

Samples

Lines the setup guide prints. A method or a class body is not a whole file. Do not replace a generated file with a partial sample. The Vue page is the full component the guide shows.

Lines the setup guide prints. A method or a class body is not a whole file. Do not replace a generated file with a partial sample. The Vue page is the full component the guide shows.

Migration

database/migrations/xxxx_xx_xx_create_posts_table.php. The up() method only. Paste it into the migration artisan created. Source

php
// database/migrations/xxxx_xx_xx_create_posts_table.php
public function up(): void
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->string('slug')->unique();
        $table->text('excerpt');
        $table->timestamp('published_at')->nullable();
        $table->timestamps();
    });
}

Model

app/Models/Post.php. The class body only. Keep the imports artisan generated. Source

php
// app/Models/Post.php
class Post extends Model
{
    use HasFactory;

    protected $fillable = ['title', 'slug', 'excerpt', 'published_at'];

    protected function casts(): array
    {
        return ['published_at' => 'datetime'];
    }
}

Factory

database/factories/PostFactory.php. The definition() method only. Source

php
// database/factories/PostFactory.php
public function definition(): array
{
    $title = fake()->sentence();

    return [
        'title' => $title,
        'slug' => str($title)->slug(),
        'excerpt' => fake()->paragraph(),
        'published_at' => now(),
    ];
}

Seeder

database/seeders/PostSeeder.php. The run() method only. It creates 12 posts. Source

php
// database/seeders/PostSeeder.php
public function run(): void
{
    Post::factory()->count(12)->create();
}

Controller

app/Http/Controllers/PostController.php. The class and the three use lines the guide shows. The Controller import is not in the sample. Source

php
// app/Http/Controllers/PostController.php
use App\Models\Post;
use Inertia\Inertia;
use Inertia\Response;

class PostController extends Controller
{
    public function index(): Response
    {
        return Inertia::render('posts/Index', [
            'posts' => Post::query()
                ->latest('published_at')
                ->get(['id', 'title', 'slug', 'excerpt', 'published_at']),
        ]);
    }
}

Route

routes/web.php. Lines to add. Leave the routes the starter kit already registered. GET /posts, named posts.index. No API route. Source

php
// routes/web.php
use App\Http\Controllers\PostController;

Route::get('/posts', [PostController::class, 'index'])->name('posts.index');

Vue page

resources/js/pages/posts/Index.vue. The full component the guide shows. The folder is pages, lowercase. The Inertia component name is posts/Index. Source

vue
<!-- resources/js/pages/posts/Index.vue -->
<script setup lang="ts">
import { Head } from '@inertiajs/vue3'

interface Post {
  id: number
  title: string
  slug: string
  excerpt: string
  published_at: string
}

defineProps<{ posts: Post[] }>()
</script>

<template>
  <Head title="Posts" />

  <main class="mx-auto max-w-3xl space-y-6 p-8">
    <h1 class="text-3xl font-semibold">Posts</h1>

    <article v-for="post in posts" :key="post.id" class="rounded-xl border p-6">
      <h2 class="text-xl font-medium">{{ post.title }}</h2>
      <p class="mt-2 text-muted-foreground">{{ post.excerpt }}</p>
    </article>
  </main>
</template>

Feature test

tests/Feature/PostsTest.php. The example the guide shows. It has no opening PHP tag. Pest. assertInertia checks the component posts/Index. Source

php
// tests/Feature/PostsTest.php
use App\Models\Post;

it('lists posts', function () {
    Post::factory()->create(['title' => 'Hello Laravel 13']);

    $this->get('/posts')
        ->assertOk()
        ->assertInertia(fn ($page) => $page
            ->component('posts/Index')
            ->has('posts', 1)
        );
});

Artisan already writes the surrounding class, the namespace, and the imports. Paste a method into that file. Paste the Vue sample as the whole page.

Setup guideMarkdown for assistants