Laravel Precognition for Vue 3
Laravel Precognition for Vue provides a frontend integration for Laravel's backend-driven "live" validation. It allows Vue 3 applications to anticipate the outcome of form submissions, primarily for real-time validation feedback as users interact with forms. The current stable version is 2.0.0, which notably moved from Axios to the native `fetch` API for network requests. This package is part of the broader Laravel ecosystem, maintaining an active development cadence with regular updates and clear versioning. Its key differentiator is its seamless integration with Laravel's validation rules, simplifying the process of displaying server-side validation errors dynamically in a Vue application without full page reloads, and reducing redundant client-side validation logic.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'errors') at useForm
cause The `useForm` function expects a method, URL, and initial data object. Providing `undefined` or an invalid structure for the initial data can cause this.fixEnsure `useForm` is called with the correct signature: `useForm('post', '/api/endpoint', { field1: '', field2: '' })` where the third argument is an object of initial form data. -
Uncaught ReferenceError: useForm is not defined
cause The `useForm` composition function was not imported from `laravel-precognition-vue`.fixAdd `import { useForm } from 'laravel-precognition-vue';` to your script section or component setup. -
Failed to load resource: the server responded with a status of 422 (Unprocessable Content) - network error in browser console without specific validation messages
cause The Laravel backend is returning validation errors, but the browser's developer console is showing a generic network error without the detailed validation messages, likely due to a missing or incorrect `Accept` header which prevents Precognition from intercepting the validation response.fixEnsure the `Accept` header is set to `application/json` for Precognition requests. Version 2.0.0 and later automatically set this header, but for custom `fetch` implementations or older versions, verify this header is present.
Warnings
- breaking Version 2.0.0 replaced Axios with the native `fetch` API for all HTTP requests. This means any custom Axios configurations, interceptors, or adapters will no longer function. Developers must adapt their code to use `fetch`-compatible patterns or provide their own `fetch` wrapper.
- breaking When upgrading from `laravel-precognition` (the core package) versions prior to v1.0.0, be aware that support for Inertia packages was removed. While `laravel-precognition-vue` is a specific integration, ensure your project setup is not relying on older core package features related to Inertia.
- gotcha Prior to v1.0.1, TypeScript support for wildcard validation paths (e.g., `data.*.name`) could be inconsistent. This might lead to type errors or incorrect inference when accessing errors for deeply nested or dynamic fields.
- gotcha Files uploaded via `form.setFile()` or similar methods are not automatically validated unless `disableFileValidation()` is explicitly enabled. If file validation is crucial, ensure it's properly configured.
Install
-
npm install laravel-precognition-vue -
yarn add laravel-precognition-vue -
pnpm add laravel-precognition-vue
Imports
- useForm
import useForm from 'laravel-precognition-vue'
import { useForm } from 'laravel-precognition-vue' - Form
import { Form } from 'laravel-precognition-vue'import type { Form } from 'laravel-precognition-vue' - Precognition errors and validation
import { validate } from 'laravel-precognition-vue'import { useForm } from 'laravel-precognition-vue'; const form = useForm(...); form.validate('field'); form.errors.field;
Quickstart
import { defineComponent, reactive } from 'vue';
import { useForm } from 'laravel-precognition-vue';
export default defineComponent({
setup() {
const form = useForm('post', '/api/register', {
name: '',
email: '',
password: '',
password_confirmation: '',
});
const submit = async () => {
try {
await form.submit();
alert('Registration successful!');
form.reset();
} catch (e) {
console.error('Submission failed:', e);
// Errors are automatically populated in form.errors
}
};
// Example of real-time validation on input change
const validateField = (field) => {
if (form[field]) { // Only validate if field has content
form.validate(field);
}
};
return { form, submit, validateField };
},
template: `
<form @submit.prevent="submit">
<div>
<label for="name">Name</label>
<input id="name" type="text" v-model="form.name" @input="validateField('name')">
<div v-if="form.errors.name">{{ form.errors.name }}</div>
</div>
<div>
<label for="email">Email</label>
<input id="email" type="email" v-model="form.email" @input="validateField('email')">
<div v-if="form.errors.email">{{ form.errors.email }}</div>
</div>
<div>
<label for="password">Password</label>
<input id="password" type="password" v-model="form.password" @input="validateField('password')">
<div v-if="form.errors.password">{{ form.errors.password }}</div>
</div>
<div>
<label for="password_confirmation">Confirm Password</label>
<input id="password_confirmation" type="password" v-model="form.password_confirmation" @input="validateField('password_confirmation')">
<div v-if="form.errors.password_confirmation">{{ form.errors.password_confirmation }}</div>
</div>
<button type="submit" :disabled="form.processing">Register</button>
<button type="button" @click="form.reset()">Reset</button>
</form>
`
});