nicholasnet's avatar

How Do You Handle Client-Side Validation in the New Inertia Form Component

I’ve been experimenting with the new Inertia Form component and really like how it simplifies handling form state, submissions, and server-side validation. However, I’m still unclear about the best way to implement client-side validation before submitting a form to server.

Most examples I’ve found focus on server-side validation triggered after the form is submitted. But what’s the recommended or most elegant pattern for validating fields on the client—things like required fields, email format checks, or real-time input validation—before hitting the server?

If you’ve already implemented this in a clean and reusable way, could you share your approach or patterns?

1 like
2 replies
martinbean's avatar

But what’s the recommended or most elegant pattern for validating fields on the client—things like required fields, email format checks, or real-time input validation—before hitting the server?

@nicholasnet By using the native types and attributes for those things?

<!-- Required input -->
<input required>

<!-- Email input -->
<input type="email">

You can also use Precognition for doing asynchronous validation on inputs: https://laravel.com/docs/precognition#using-vue-and-inertia

1 like
Joey33's avatar

I've been trying to implement tanstack from shadcn but had issues with displaying errors returned from failed validation by laravel so ended up with useForm from Inertia and zod for client-side. Works like a charm. Here you are:

import * as z from 'zod';

rules:

...
const LessonSchema = z.object({
    title: z
        .string()
        .min(5, 'Title must be at least 5 characters.')
        .max(10, 'Title must be at most 32 characters.')
    description: z
        .string()
        .min(20, 'Description must be at least 20 characters.')
        .max(100, 'Description must be at most 100 characters.'),
});

later in the form component closure

form fields

 <div>
                            <label htmlFor="title" className="mb-1 block text-sm font-medium text-gray-700">
                                Title
                            </label>
                            <input
                                type="text"
                                id="title"
                                value={data.title}
                                // Clear client-side error when user starts typing
                                onChange={(e) => {
                                    setData('title', e.target.value);
                                    if (clientErrors.title) {
                                        setClientErrors((prev) => ({ ...prev, title: undefined }));
                                    }
                                }}
                                className={`w-full rounded-lg border p-3 shadow-sm transition duration-150 ${
                                    isError('title')
                                        ? 'border-red-500 focus:ring-red-500'
                                        : 'border-gray-300 focus:ring-green-500'
                                }`}
                                onBlur={() => validateData(data)} // Validate on blur for immediate feedback
                            />
                            {getError('title') && <p className="mt-1 text-sm text-red-600">{getError('title')}</p>}
                        </div>

Please or to participate in this conversation.