Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Starter Kit Inertia React Laravel Package

nunomaduro/laravel-starter-kit-inertia-react

Official Laravel starter kit by Nuno Maduro pairing Inertia.js with React, TypeScript, Tailwind CSS, and shadcn/ui. It ships with authentication, a polished dashboard layout, and sensible defaults so you can skip boilerplate and start building your next Laravel app fast.

View on GitHub
Deep Wiki
Context7

Getting Started

What you are actually adopting

This is not a package you composer require into an existing app — it is an opinionated skeleton. You adopt it by cloning or creating a project from it, then living inside its conventions. That distinction matters day-to-day: nearly every "how do I do X here?" question is answered by looking at a sibling file rather than by reading package docs.

First five minutes

  1. Read AGENTS.md (and the .ai/ rules it points at) before touching code. It encodes the non-negotiables: Action pattern, declare(strict_types=1), final readonly classes, PHP 8 constructor promotion, explicit return types, TitleCase enum cases.
  2. Boot the stack. The intended loop is a single command that runs PHP + Vite together:
    composer run dev
    
    If you only see stale UI, you need npm run dev or npm run build — this is the single most common "my change didn't apply" cause.
  3. Run the verification suite once, cold, to see what green looks like:
    php artisan test --compact
    vendor/bin/pint --test
    vendor/bin/phpstan analyse   # Larastan
    vendor/bin/rector --dry-run
    
  4. Skim the generated frontend glue: resources/js/pages, and the Wayfinder output directories (@/actions, @/routes). You will not hand-write URLs in this codebase.

Orientation map

Concern Look here first
Business logic app/Actions
Auth flows app/Actions/Fortify, FortifyServiceProvider, config/fortify.php
Pages / screens resources/js/pages
Typed route calls @/actions, @/routes, vite.config.js (wayfinder plugin)
Rules for AI + humans AGENTS.md, .ai/*
Quality gates composer.json scripts, phpstan.neon, rector.php, pint.json

Your first real use case

The canonical end-to-end slice is: Action → Controller → Wayfinder function → React form.

<?php

declare(strict_types=1);

namespace App\Actions;

final readonly class CreateFavorite
{
    public function handle(User $user, string $favorite): bool
    {
        // DB::transaction() here if multiple models are involved
        return $user->favorites()->create(['name' => $favorite]) !== null;
    }
}

Scaffold it with php artisan make:action "CreateFavorite" --no-interaction, not by hand — the generator emits the correct final readonly shape and strict types. Then call it from a controller that Inertia::render()s, and consume the route from React via Wayfinder. That one loop exercises every layer of the kit.


Implementation Patterns

The Action pattern is the spine

Actions are the default home for logic, and they are deliberately context-free: the same class gets called from HTTP controllers, queued jobs, Artisan commands, MCP tools, and API endpoints. Practical consequences:

  • Keep handle() as the only public entry point. If you feel the urge to add a second public method, you probably have two actions.
  • Inject collaborators as private promoted constructor properties; skip the constructor entirely when there are none (an empty zero-arg __construct() is explicitly disallowed).
  • Wrap multi-model writes in DB::transaction() inside the action, not in the controller — otherwise a job calling the same action bypasses the transaction.
  • Actions are named for what they do, no Action/Service suffix: CreateFavorite, UpdateUserProfileInformation, SendInvoice.

This pays off in testing: a unit test against handle() covers every caller at once, which is why the kit can demand "every change must be programmatically tested" without the suite becoming unbearable.

Wayfinder: never type a URL again

Wayfinder generates typed functions from your Laravel routes, so the frontend breaks at compile time when a route changes, not at runtime.

import { store } from '@/actions/App/Http/Controllers/FavoriteController';
import { index } from '@/routes/favorites';

// link
<a href={index.url()}>Favorites</a>

// form submit
<form onSubmit={store.form({ method: 'post' })}>...</form>

Working rules:

  • Reach for @/actions/@/routes before fetch('/favorites'). Hardcoded URLs are the thing this kit exists to eliminate.
  • Prefer .url() for hrefs/navigation, .form() for Inertia <Form>/form props, .get()/.post() for programmatic calls.
  • Query params and route-model binding are passed as typed arguments — let TypeScript tell you the shape rather than reading the controller by hand.
  • Generated output is regenerated, never edited. wayfinder:generate (and the Vite plugin during dev) owns those files; treat them like vendor/.
  • Tree-shaking means only the routes you import land in the bundle — so import narrowly rather than from a barrel.

Inertia v3 in practice

The v3 surface is where most day-to-day gains live:

  • Deferred props for slow panels (activity feeds, stats). The contract is that you must ship a skeleton/empty state with a pulse animation — an empty div is a bug, not a placeholder.
  • Optimistic updates with automatic rollback for toggles, likes, reordering. Write the optimistic state, let Inertia revert on failure; don't hand-roll a try/catch that restores previous state.
  • useHttp for standalone requests that aren't visits (typeahead, autosave). Note Axios is gone — use the built-in XHR client and interceptors rather than adding Axios back casually.
  • useLayoutProps so persistent layouts (nav, sidebar) receive props without every page re-declaring them.
  • Polling / prefetching / merging props replace a lot of bespoke setInterval and manual state merging.

Server side stays boring on purpose: a controller method calls an Action and returns Inertia::render('favorites/index', [...]). Prop types (optional(), defer(), merge()) compose into nested arrays via dot-notation, which is handy for wrapping deferred data in a larger payload.

Auth is Fortify, and it is already wired

Do not build login/registration by hand. Fortify registers the routes and controllers; you customize at the seams:

  • User creation → app/Actions/Fortify/CreateNewUser
  • Profile updates → UpdateUserProfileInformation
  • Redirects → implement/bind a LoginResponse
  • 2FA, password confirmation, throttling → config/fortify.php + FortifyServiceProvider

The React side consumes these through Wayfinder route functions, so enabling 2FA is a config + UI task, not a routing task.

Test-driven by contract

Feature tests are the default (php artisan make:test, --unit for the rare unit test); use factories and their named states before hand-building models. Run narrow, not wide:

php artisan test --compact tests/Feature/CreateFavoriteTest.php

The kit's philosophy — every change gets a test, and the minimum number of tests runs — only works if you keep tests scoped. Reach for php artisan tinker --execute '...' with single quotes for one-off inspection; don't leave verification scripts behind.

Let Boost and the skills carry the context

The MCP tooling is not decoration:

  • search-docs before code changes — it returns version-specific docs for the exact installed majors (Laravel 13, Inertia v3, Fortify v1). Passing a packages array scopes results; queries are auto-stemmed AND, so use "quoted phrases" for adjacency and multiple queries for OR.
  • database-schema before writing a migration or model; database-query instead of raw SQL in tinker.
  • get-absolute-url before you paste a link to a teammate.
  • browser-logs for frontend exceptions — recent entries only.

The three domain skills (laravel-best-practices, wayfinder-development, fortify-development) are meant to be activated on entry to the domain, not after you're stuck. In a strict codebase, that early activation is what keeps you from inventing a fourth way to do something.


Gotchas and Tips

Strictness will fight your muscle memory

PHP 8.5 + Pint + Rector + Larastan at level-high means sloppy code doesn't survive composer scripts. Expect:

  • Pint to reformat and Rector to rewrite on every run — commit formatting separately from logic so diffs stay reviewable.
  • Larastan to reject untyped arrays and loose returns. Reach for array-shape PHPDoc (@param array{id: int, name: string} $data) rather than array.
  • The "no superfluous annotations" rule: don't sprinkle @var/@return where the signature already says it. Only @-prefixed typing annotations that add information.
  • Always braces on control structures, always explicit return types and parameter types. No exceptions, even for one-liners.

Inertia v2/v3 removals that bite during upgrades

  • Inertia::lazy() / LazyProp is removed → Inertia::optional().
  • router.cancel() → router.cancelAll().
  • Events renamed: invalid → httpException, exception → networkError.
  • The future config namespace is gone; all v2 future options are always on. Any config referencing it is dead weight.
  • Axios is not bundled. Don't assume window.axios exists.

Deferred props without skeletons

The kit treats a missing skeleton as a defect. If you add Inertia::defer(...), add the pulsing placeholder in the same commit — otherwise reviewers (and the guidelines) will bounce it. Same for optimistic updates: verify the rollback path, since the happy path hides it.

Wayfinder confusion

  • TypeScript errors about missing route functions are usually a stale generation problem: re-run wayfinder:generate or restart npm run dev.
  • Don't mix generated imports with string URLs in the same feature; once you hardcode one path, the compile-time safety guarantee is a lie for that route.
  • Route model binding arguments are typed — pass the identifier the route expects, not the model object from another source.

The Vite manifest error

Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest means no build and no dev server. npm run build, or start npm run dev/composer run dev. In v3 dev mode, SSR is handled by the @inertiajs/vite plugin automatically — you don't run a separate Node SSR server locally, so don't go looking for one when debugging rendering.

Architecture etiquette

  • Actions belong in app/Actions — don't create parallel Services/ or Support/ base folders without approval.
  • Don't swap dependencies on a whim; the versions listed in the guidelines are a curated set (Laravel 13, Inertia v3, Pest 5, PHPUnit 13, Rector 2, Larastan 3, Boost 2). Adding a package to a "refuse to compromise" skeleton is a design decision, not a convenience.
  • Don't create documentation files unless asked. The rules live in AGENTS.md; a second source of truth rots.
  • Reuse check: scan for an existing component before writing a new one, and follow sibling files rather than inventing a structure. In a skeleton this small, divergence is immediately visible.

Extension points worth knowing

Need Extend here
New business capability app/Actions/{Verb}{Noun} via make:action
New auth behaviour Fortify actions + LoginResponse binding
New screen resources/js/pages + Inertia::render() + Wayfinder import
New quality gate composer.json scripts (keep --compact/narrow runs fast)
New AI guidance .ai/ rules consumed by AGENTS.md

The through-line: this kit trades flexibility for a single obvious way to do each thing. Leverage it by generating rather than hand-writing, importing rather than hardcoding, and testing the action rather than every caller.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource