Back to Course |
React.js + Inertia in Laravel 11: From Scratch

Form Validation and Multi-Submit Prevention

Our form doesn't have validation, so let's add it in this lesson.


Back-End Validation

First, let's add the validation on the back-end. For this, we will use Form Request.

php artisan make:request StorePostRequest

app/Http/Requests/StorePostRequest.php:

class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
 
public function rules(): array
{
return [
'title' => 'required|min:3',
'content' => 'required',
];
}
}

app/Http/Controllers/PostController.php:

use App\Http\Requests\StorePostRequest;
 
class PostController extends Controller
{
// ...
 
public function store(Request $request): RedirectResponse
public function store(StorePostRequest $request): RedirectResponse
{
Post::create([
'title' => $request->input('title'),
'content' => $request->input('content'),
]);
Post::create($request->validated());
 
return redirect()->route('posts.index');
}
}

We don't need anything else on the back-end.


Showing Errors on the Front-End

For the front-end, there are a couple of ways to get errors. One way is to get errors from the form helper.

resources/js/Pages/Posts/Create.jsx:

import AppLayout from '../../Layouts/AppLayout.jsx';
import { Head, Link, useForm } from '@inertiajs/react';
 
export default function CreatePost() {
// ...
 
return (
<AppLayout>
<Head title="New Post" />
 
<div>
<form onSubmit={submit}>
<div>
<label htmlFor="title" className="block text-sm font-medium text-gray-700">Title</label>
<input onChange={(e) => setData('title', e.target.value)} type="text" id="title" className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" />
{ errors.title && (<p className="mt-2 text-sm text-red-600">{errors.title}</p>)}
</div>
<div className="mt-4">
<label htmlFor="content" className="block text-sm font-medium text-gray-700">Content</label>
<textarea onChange={(e) => setData('content', e.target.value)} type="text" id="content" className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"></textarea>
{ errors.content && (<p className="mt-2 text-sm text-red-600">{errors.content}</p>)}
</div>
<div className="mt-4 py-4 space-x-2">
<button type="submit" className="inline-block rounded-md bg-blue-500 px-4 py-3 text-xs font-semibold uppercase tracking-widest text-white shadow-sm">
Save post
</button>
<Link href={route('posts.index')} className="inline-block rounded-md border border-gray-300 px-4 py-3 text-xs font-semibold uppercase tracking-widest shadow-sm"> Cancel
</Link>
</div>
</form>
</div>
</AppLayout>
);
}

It's that simple. We can see the validation message in the front-end.


Multi-Submit Prevention

Another feature of the Inertia form helper is that we can easily disable the submit button to prevent multiple submits.

If the back-end takes a while to process the form, you can disable the button. Form helper have a processing method.

resources/js/Pages/Posts/Create.jsx:

import AppLayout from '../../Layouts/AppLayout.jsx';
import { Head, Link, useForm } from '@inertiajs/react';
 
export default function CreatePost() {
// ...
 
return (
<AppLayout>
<Head title="New Post" />
 
<div>
<form onSubmit={submit}>
// ...
<div className="mt-4 py-4 space-x-2">
<button type="submit" className="inline-block rounded-md bg-blue-500 px-4 py-3 text-xs font-semibold uppercase tracking-widest text-white shadow-sm">
<button disabled={processing} type="submit" className="inline-block rounded-md bg-blue-500 px-4 py-3 text-xs font-semibold uppercase tracking-widest text-white shadow-sm disabled:opacity-25">
Save post
</button>
<Link href={route('posts.index')} className="inline-block rounded-md border border-gray-300 px-4 py-3 text-xs font-semibold uppercase tracking-widest shadow-sm"> Cancel
</Link>
</div>
</form>
</div>
</AppLayout>
);
}

For example, we could add sleep in the Controller to make the request longer.

app/Http/Controllers/PostController.php:

class PostController extends Controller
{
// ...
 
public function store(StorePostRequest $request): RedirectResponse
{
sleep(2);
Post::create($request->validated());
 
return redirect()->route('posts.index');
}
}

After pressing the Save post button, you cannot click it again.