Live validation with Laravel Precognition and Vue

Inertia's Form component already validates one field as the user leaves it. A precognitive request runs the rules and does not run the controller.

For your AI agent

Copies a prompt. Paste it into the AI agent you already have open.

Read https://laravel-vuejs.space/laravel-precognition-vue.md
Inertia's Form component already validates one field as the user leaves it. A precognitive request runs the rules and does not run the controller.
Show the diff. Do not commit.

Next: Set the Inertia document title in Vue

On this page · 8 sections
  1. The server still has to opt in
  2. The Vue slot
  3. Files and expensive rules
  4. Where this stops
  5. Recap
  6. FAQ
  7. Next steps
  8. Sources

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 HandlePrecognitiveRequests to store/update.
  • A precognitive request validates and stops: the controller never runs.
  • In Vue: @change="validate('title')", then invalid('title') and errors.title.
  • Files are skipped unless you set validate-files; skip required with isPrecognitive().
  • No laravel-precognition-vue-inertia package needed on Inertia 3.

What you'll learn

  • How to opt a route into precognitive requests
  • How to validate fields live with the <Form> slot
  • How to handle files and expensive rules

Who it's for: Laravel + Vue developers with forms on Inertia 3

Applies to: Laravel 13 · Inertia 3 · Vue 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.

bash
php artisan make:request StorePostRequest
app/Http/Requests/StorePostRequest.phpphp
public function rules(): array
{
    return [
        'title' => ['required', 'string', 'max:255'],
        'excerpt' => ['required', 'string'],
    ];
}
app/Http/Controllers/PostController.phpphp
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.

resources/js/pages/posts/Create.vuevue
<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.

app/Http/Requests/StorePostRequest.phpphp
'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.

Next steps#

Sources#

Found this useful?

Ask questions, share what you built.

Join thousands of Laravel and Vue developers in our Facebook group, or get new posts in your Telegram feed. Sharing this article helps too.

Keep reading

Newsletter

Get the best of Laravel & Vue, once a month.

One short email with new guides, release notes worth reading and hand-picked community links. No spam, unsubscribe any time.