# 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.

- URL: https://laravel-vuejs.space/laravel-precognition-vue
- Author: Anass Ez-zouaine (https://laravel-vuejs.space/author/anass-ez-zouaine)
- Published: 2026-10-10
- Topics: Laravel, VueJs, Inertia

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](https://laravel-vuejs.space/laravel-inertia-vue-crud) 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](https://laravel-vuejs.space/laravel-vue-in-2026).

## 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.php`
```php
public function rules(): array
{
    return [
        'title' => ['required', 'string', 'max:255'],
        'excerpt' => ['required', 'string'],
    ];
}
```

`app/Http/Controllers/PostController.php`
```php
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.vue`
```vue
<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](https://laravel-vuejs.space/laravel-vue-file-upload).

`app/Http/Requests/StorePostRequest.php`
```php
'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](https://laravel-vuejs.space/laravel-vue-permissions) or a [policy](https://laravel-vuejs.space/laravel-vue-policies). 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

**Q: 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.

**Q: Does Precognition run my controller?**

No. It runs route middleware and validation, then stops before the controller method.

**Q: 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

- [CRUD with Inertia and Vue](https://laravel-vuejs.space/laravel-inertia-vue-crud)
- [Upload files with Laravel and Vue](https://laravel-vuejs.space/laravel-vue-file-upload)
- [Show Laravel validation errors in Vue](https://laravel-vuejs.space/laravel-vue-validation-errors)

## Sources

- [Laravel 13: Precognition](https://laravel.com/docs/13.x/precognition)
- [Inertia v3: Forms](https://inertiajs.com/docs/v3/the-basics/forms)
