ホーム > Laravel > What Is Laravel Precognition? Building Real-Time Form Validation with Vue + Inertia
Laravel

What Is Laravel Precognition? Building Real-Time Form Validation with Vue + Inertia

Thank you for your continued support.
This article contains advertisements that help fund our operations.

How Laravel's built-in Precognition feature works, and a hands-on build of a real-time validation form with Vue + Inertia to see it in action.

関連動画

Related Video

This is a video where we actually tried out the content from the article! If anything is unclear in the article, please check out the video.

The video provides further explanations and demonstrations, so it should be helpful.

Subscribe to Our Channel

If you found this video helpful, please consider subscribing to our channel or giving it a thumbs up! It really motivates us to create more content.

Questions and Feedback

If you have any questions or feedback regarding this article or the video, feel free to leave them in the comment section of the video. Your input is greatly appreciated and will help us improve our content in the future!

Precognition, in short

It lets you run your backend validation ahead of time, without actually submitting or saving the form.

It sidesteps the usual SPA problem of maintaining "frontend input checks" and "backend validation rules" as two separate things, and it can reflect rules in real time even when they need a database lookup — like checking whether an email address is already taken.

What is Precognition?

Precognition is built into Laravel core, so the backend side needs no extra package.

Laravel 13.x Precognition

The usual approach to validation looks like this:

  • Watch the input with JavaScript and show an error if it's invalid
  • Submit → validate on the server → return an error if there is one

That means implementing the frontend and backend checks separately. With Precognition, you can ask the server "is this field valid right now?" while the user is still typing.

The key point is that you get to reuse the exact same rules from the FormRequest you're already using.

There's no need to reimplement the same rules on the frontend, and rules that need a DB lookup, like unique, run for real at precognition time too.

Pretty convenient.

I actually built an environment and tried it

Rather than just reading the docs, I set up a Laravel + Vue + Inertia starter-kit environment in Docker and built one working form.

1. Create a project with the Vue + Inertia starter kit

laravel new laravel-precognition-demo --vue --database=sqlite --no-authentication --pest

This pulled in Laravel 13.26.1 and Inertia 3.x as of this writing.

2. The backend side needed no extra package

All I had to do was add HandlePrecognitiveRequests to the web middleware group in bootstrap/app.php.

use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests; // added

->withMiddleware(function (Middleware $middleware): void {
    $middleware->web(append: [
        HandleInertiaRequests::class,
        AddLinkHeadersForPreloadedAssets::class,
        HandlePrecognitiveRequests::class, // added
    ]);
})

Looking inside vendor/laravel/framework, Illuminate\Foundation\Http\FormRequest already had the Precognition-handling code built in, so that one line was all it took.

3. Built an event sign-up form

For testing, I built a simple form that registers a name, email address, and number of guests.

A unique rule is something the frontend alone can never judge on its own, so I figured it would make Precognition's effect easy to see and added one.

// app/Http/Requests/StoreSignupRequest.php
public function rules(): array
{
    return [
        'name' => ['required', 'string', 'max:255'],
        'email' => ['required', 'email', 'max:255', 'unique:signups,email'],
        'guests' => ['required', 'integer', 'min:1', 'max:5'],
    ];
}

The controller and route are just ordinary Laravel + Inertia code.

// app/Http/Controllers/SignupController.php
public function store(StoreSignupRequest $request): RedirectResponse
{
    Signup::create($request->validated());

    return redirect()->route('signup.create')->with('success', 'お申し込みありがとうございます。');
}

4. Installing the frontend package

npm install laravel-precognition-vue-inertia --legacy-peer-deps

Why I needed --legacy-peer-deps

When I first tried:

npm install laravel-precognition-vue-inertia

npm install failed outright.

npm error Found: @inertiajs/[email protected]
npm error Could not resolve dependency:
npm error peer @inertiajs/vue3@"^1.0.0 || ^2.0.0" from [email protected]

A version mismatch.

As of this writing, the latest [email protected] only allows @inertiajs/vue3 v1 or v2 in its peerDependencies, and it doesn't officially line up with the Inertia v3 that the current starter kit ships with.

Adding --legacy-peer-deps gets the install through, and as you'll see below it worked fine at runtime, so there doesn't seem to be any real harm — but it's worth knowing that the Vue helper for Precognition hasn't caught up to Inertia v3 yet.

Inertia, or plain axios?

Precognition's frontend helpers come in two flavors:

  1. laravel-precognition-vue — for plain Vue
  2. laravel-precognition-vue-inertia — for Vue + Inertia

Since this project uses Inertia, I went with the latter, but depending on your setup you may need the former instead.

5. The Vue side

Swap useForm for the Precognition version, call form.validate() on each input's change event, and real-time validation just works.

<script setup lang="ts">
//import { useForm } from "@inertiajs/vue3"; // regular Inertia uses this
import { useForm } from "laravel-precognition-vue-inertia"

const form = useForm("post", "/signup", {
  name: "",
  email: "",
  guests: 1,
})
</script>

<template>
  <input v-model="form.email" @change="form.validate('email')" />
  <p v-if="form.invalid('email')">{{ form.errors.email }}</p>
</template>

form.invalid('email') tells you whether that field currently has an error, and form.errors carries the exact validation messages from the FormRequest, which is what gets displayed here:

<p v-if="form.invalid('email')">{{ form.errors.email }}</p>

Actually running it and checking the behavior

I started the server with php artisan serve and took screenshots with a headless browser while reproducing real form interactions.

The initial state.

laravel precognition 01 initial

Type an invalid email format and blur the field, and an error shows up immediately — without ever submitting.

laravel precognition 02 invalid email

Type an email address that's already registered, and the same real-time "already taken" error appears.

laravel precognition 03 duplicate email

This is a check that's simply impossible to reproduce with frontend-only input validation — it involves a real round trip to the database.

Fix it to an unregistered address, and the error clears.

laravel precognition 04 valid

Submitting from there saves to the database as usual, and a success message shows up.

laravel precognition 05 success

Looking at the actual request/response traffic

I logged the browser's network traffic directly to see how the Precognition headers are actually used.

ActionPrecognition-Validate-Only (request)StatusPrecognition-Success (response)
Filled in name onlyname204true
Typed an invalid email formatemail,name422(none)
Typed an already-registered emailemail,name422(none)
Fixed to an unregistered emailemail,name204true
Submit (a regular POST)-302-

I found that Precognition-Validate-Only doesn't just carry the field you just touched — it accumulates every field you've touched so far, comma-separated (touch name then email, and it becomes email,name).

When there are no errors you get a bodiless 204; when there are, you get a regular 422 in the same shape as a normal validation error.

Things I noticed / watch out for

The debounce defaults to 1500ms

"Debounce" means the client doesn't send a request to the server on every single keystroke — instead it waits until input has paused for a while, then sends just one request.

Looking inside the laravel-precognition package (validator.js), the default value was debounceTimeoutDuration = 1500.

You can change it with form.setValidationTimeout(), like this:

<script setup lang="ts">
    import { useForm } from 'laravel-precognition-vue-inertia';

    const form = useForm('post', '/signup', {
        name: '',
        email: '',
        guests: 1,
    });

    form.setValidationTimeout(300); // call this once, right after creating the form
</script>

authorize() runs too

According to the official docs, a Precognition request still evaluates the FormRequest's authorize() as usual.

Validation effectively runs twice

Both the precognitive request and the real submission go through the actual FormRequest, so rules that hit the database, like unique, add extra server load.

Worth watching if you're attaching a heavy rule to a field that gets hit frequently.

The official frontend package doesn't yet support Inertia v3 (as of this writing)

As mentioned above, [email protected]'s peerDependencies only go up to Inertia v1/v2, so --legacy-peer-deps was necessary with the current starter-kit setup.

Summary

I came away thinking this is genuinely convenient.

For the sake of UX, it's friendlier to validate before the user even hits submit, so I've always ended up hand-rolling frontend checks that try to stay in sync with the backend.

Being able to reuse the rules written in one place — the FormRequest — for frontend validation too was a real win.

It doesn't require a big rewrite either, so it should drop into an existing project easily.

It even works with a slightly older project still using axios, which is nice.

The version lag on the frontend package is worth keeping an eye on if you're using the latest Laravel.

Thanks for reading all the way through.

Please Provide Feedback
We would appreciate your feedback on this article. Feel free to leave a comment on any relevant YouTube video or reach out through the contact form. Thank you!