Users fill in a long form, hit Save, and only then learn the title was too long. Precognition runs your real Laravel rules as they type, without running the controller. Inertia 3’s <Form> already supports it; here is the server opt-in and the Vue slot.
TL;DR
- Put rules in a form request; add
HandlePrecognitiveRequeststostore/update. - A precognitive request validates and stops: the controller never runs.
- In Vue:
@change="validate('title')", theninvalid('title')anderrors.title. - Files are skipped unless you set
validate-files; skiprequiredwithisPrecognitive(). - No
laravel-precognition-vue-inertiapackage needed on Inertia 3.
The CRUD form shows errors after submit. Precognition shows them when the field changes, using the same Laravel rules. Inertia 3’s <Form> component already does this. Older tutorials install laravel-precognition-vue-inertia and call useForm from that package. You do not need that package on a current Inertia app. Remove it if a previous tutorial added it.
This page does not cover optimistic UI. The form still waits for the server. The stack choice is Inertia, a SPA, or Nuxt.
The server still has to opt in#
Heads up: validation only, no insert
A precognitive request runs the route middleware and the validation. It does not run the controller method. Typing in the title field must not insert a post. Laravel only treats the request that way when HandlePrecognitiveRequests is on the route.
Put the rules in a form request so the precognitive call and the real submit share them.
php artisan make:request StorePostRequestpublic function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'excerpt' => ['required', 'string'],
];
}use App\Http\Requests\StorePostRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
class PostController extends Controller implements HasMiddleware
{
public static function middleware(): array
{
return [
new Middleware(HandlePrecognitiveRequests::class, only: ['store', 'update']),
];
}
public function store(StorePostRequest $request): RedirectResponse
{
Post::create([
'title' => $request->string('title')->toString(),
'excerpt' => $request->string('excerpt')->toString(),
'slug' => str($request->string('title')->toString())->slug(),
'published_at' => now(),
]);
return to_route('posts.index');
}
}store only runs on the real submit. The live check stops at StorePostRequest.
The Vue slot#
validate('title') sends the precognitive request. invalid('title') is true only after a response says the field failed. validating is true while that request is in flight. A field that still has its initial value does not send a request.
<Form
action="/posts"
method="post"
#default="{ errors, invalid, validate, validating }"
class="space-y-4"
>
<div>
<label for="title">Title</label>
<input id="title" name="title" @change="validate('title')" class="w-full border px-3 py-2" />
<p v-if="invalid('title')">{{ errors.title }}</p>
</div>
<div>
<label for="excerpt">Excerpt</label>
<textarea id="excerpt" name="excerpt" @change="validate('excerpt')" class="w-full border px-3 py-2" />
<p v-if="invalid('excerpt')">{{ errors.excerpt }}</p>
</div>
<p v-if="validating">Checking...</p>
<button class="border px-4 py-2">Save</button>
</Form>Watch the precognitive request
Open DevTools → Network, type a 300-character title and tab out. You will see a request with a Precognition: true header come back 422, and no new row in the database.
Import Form from @inertiajs/vue3, the same component the CRUD guide uses. Submit still posts the form. You do not add a second @submit handler.
Files and expensive rules#
Gotcha: required files fail every live check
Precognition leaves files out of the live request unless you set validate-files on <Form>, or call validateFiles() on useForm. With the default, a rule that says cover is required fails every live check, because the file is not in that request. Skip the required rule while the request is precognitive, and keep the image rules for the real submit. The field itself is the upload guide.
'cover' => [
...$this->isPrecognitive() ? [] : ['required'],
'image',
'max:2048',
],max:2048 is kilobytes. The same split works for a password check you only want on submit, such as an uncompromised-password lookup. Read $this->isPrecognitive() inside the form request and choose the cheaper rule for the live call.
Where this stops#
The errors after a normal submit are still the CRUD form. Who is allowed to submit is a permission or a policy. Precognition does not replace either one.
Recap#
- Form request +
HandlePrecognitiveRequests= live validation with your real rules. - The controller only runs on the real submit.
- Use the
<Form>slot helpers; no extra package. - Treat files and expensive rules differently with
isPrecognitive().
FAQ#
Do I need laravel-precognition-vue-inertia with Inertia 3?
No. Inertia 3’s <Form> component supports precognition. Remove the old package if a tutorial added it.
Does Precognition run my controller?
No. It runs route middleware and validation, then stops before the controller method.
Why does my required file field always fail live validation?
Files are not sent in precognitive requests by default. Skip required when $this->isPrecognitive() is true, or set validate-files.



