Precognition¶
Real-time form validation without executing handler side effects.
See also
Official Laravel Precognition docs: Precognition
Overview¶
Precognition allows you to validate form inputs in real-time as users type, without executing the route handler’s main logic. This provides instant feedback while preventing side effects like database writes or emails.
The protocol works by:
Frontend sends a validation-only request with
Precognition: trueheaderServer runs Litestar validation (DTOs, Pydantic, dataclasses, attrs, TypedDicts, msgspec, etc.) but skips the handler body
Returns 204 on success or 422 with validation errors
Handler only executes on final form submission (without Precognition header)
Configuration¶
Enable Precognition support in your InertiaConfig:
from litestar_vite.config import InertiaConfig
InertiaConfig(
precognition=True, # Enable Precognition support
)
When enabled, the plugin automatically formats validation errors in Laravel’s
format (422 responses). To prevent handler execution on successful validation
(204 responses), add the @precognition decorator to your route handlers.
Backend Usage¶
Add the @precognition decorator to route handlers that should support
real-time validation:
from litestar import post
from litestar_vite.inertia import precognition, InertiaRedirect
@post("/users")
@precognition
async def create_user(request: Request, data: UserCreate) -> InertiaRedirect:
# This code only runs for actual form submissions
# Precognition validation requests return 204 automatically
user = await User.create(**data.model_dump())
return InertiaRedirect(request, f"/users/{user.id}")
The decorator:
Returns 204 No Content with
Precognition-Success: truewhen validation passesSkips the handler body entirely for Precognition requests
Allows normal execution for regular requests
Validation errors are formatted automatically by the exception handler.
Frontend Usage¶
Use the official laravel-precognition-vue or laravel-precognition-react
libraries on the frontend:
Install the library:
npm install laravel-precognition-react
Use the form helper:
import { useForm } from "laravel-precognition-react";
export default function CreateUser() {
const form = useForm("post", "/users", {
name: "",
email: "",
});
function submit(e: React.FormEvent) {
e.preventDefault();
form.submit();
}
return (
<form onSubmit={submit}>
<input
value={form.data.name}
onChange={(e) => form.setData("name", e.target.value)}
onBlur={() => form.validate("name")}
/>
{/* Validate on blur */}
{form.errors.name && <span>{form.errors.name}</span>}
<input
value={form.data.email}
onChange={(e) => form.setData("email", e.target.value)}
onBlur={() => form.validate("email")}
/>
{form.errors.email && <span>{form.errors.email}</span>}
<button disabled={form.processing}>Create</button>
</form>
);
}
Install the library:
npm install laravel-precognition-vue
Use the form composable:
<script setup lang="ts">
import { useForm } from "laravel-precognition-vue";
const form = useForm("post", "/users", {
name: "",
email: "",
});
function submit() {
form.submit();
}
</script>
<template>
<form @submit.prevent="submit">
<input
v-model="form.name"
@change="form.validate('name')"
/>
<span v-if="form.errors.name">{{ form.errors.name }}</span>
<input
v-model="form.email"
@change="form.validate('email')"
/>
<span v-if="form.errors.email">{{ form.errors.email }}</span>
<button :disabled="form.processing">Create</button>
</form>
</template>
Partial Field Validation¶
Precognition supports validating individual fields via the
Precognition-Validate-Only header. The frontend libraries handle this
automatically when you call form.validate("fieldName").
The server only returns errors for the specified fields, reducing noise during real-time validation.
Request Properties¶
InertiaRequest provides properties for Precognition:
request.is_precognition # True if Precognition header present
request.precognition_validate_only # Set of field names to validate
Rate Limiting¶
Warning
Real-time validation can generate many requests. Consider throttling.
Laravel has no official rate limiting solution for Precognition. The frontend libraries include debouncing (typically 300-500ms), but you should still consider server-side protection:
Separate rate limits: Consider higher limits for Precognition requests
Field-level throttling: Limit requests per field per session
Abuse prevention: Monitor for excessive validation attempts
Example rate limiting approach:
# You could check for Precognition header in a custom rate limiter
# to apply different limits for validation vs. submission requests
Error Format¶
Precognition responses use Laravel’s validation error format for compatibility:
{
"message": "The given data was invalid.",
"errors": {
"email": ["The email field is required."],
"password": ["The password must be at least 8 characters."]
}
}
This format is expected by the laravel-precognition-vue and
laravel-precognition-react libraries.
TypeScript Types¶
Types are available from the plugin:
import {
PrecognitionHeaders,
PrecognitionValidationErrors,
PrecognitionFormConfig,
isPrecognitionSuccess,
isPrecognitionError,
extractPrecognitionErrors,
} from "litestar-vite-plugin/inertia-helpers";
// Check response types
if (isPrecognitionSuccess(response)) {
// Validation passed (204)
}
if (isPrecognitionError(response)) {
const errors = await extractPrecognitionErrors(response);
console.log(errors.errors); // { email: ["..."], ... }
}
See Also¶
Forms - Form handling basics
Laravel Precognition - Official protocol docs
laravel-precognition-vue - Vue frontend library
laravel-precognition-react - React frontend library