Courses

React.js + Inertia in Laravel 11: From Scratch

Create Post Form: Component and Submit in SPA

Summary of this lesson:
- Create post creation form in React
- Implement Inertia form handling
- Add route and controller for post creation
- Use Inertia form helper for submission
- Maintain SPA navigation experience

In the last lesson, we stopped before creating the login form. But, to create the login form, we need to get familiar with forms and how they work in Inertia in general.

We will do that based on another form: we will create the "Add new post" form and apply that form to the login form in the following lessons a bit later.


Preparation: React Component, Route and Controller

First, we need to create a React component where the form will be, then the route, and then we need to submit the form.

**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 here
</div>
</AppLayout>
);
}

In the routes, we will transform the post route into a resource.

routes/web.php:

Route::get('posts', [PostController::class, 'index'])->name('posts.index');
Route::resource('posts', PostController::class);
Route::inertia('about', 'About')->name('about');
Route::inertia('login', 'Pages/Login')->name('login');

In the PostController we need to return Inertia in the create() method.

app/Http/Controllers/PostController.php:

use Inertia\Inertia;
use Inertia\Response;
 
class PostController extends Controller
{
//
 
public function create(): Response
{
return Inertia::render('Posts/Create');
}
}

After visiting the posts/create page, we see the page with the Form here text.


Add Link to the Form

Next, add a link above the posts table to the create post page.

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

import AppLayout from '../../Layouts/AppLayout.jsx';
import { Head } from '@inertiajs/react';
import { Head, Link } from '@inertiajs/react';
 
export default function PostsIndex({ posts }) {
return (
<AppLayout>
<Head>
<title>Posts</title>
</Head>
 
<div>
<Link href={route('posts.create')} className="mb-4 inline-block rounded-md bg-blue-500 px-4 py-3 text-xs font-semibold uppercase tracking-widest text-white shadow-sm">
Add new post
</Link>
 
<table className="min-w-full divide-y divide-gray-200 border">
// ...
);
};


Build The Form

Now, let's add the form.

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

import AppLayout from '../../Layouts/AppLayout.jsx';
import { Head, Link } from '@inertiajs/react';
 
export default function CreatePost() {
return (
<AppLayout>
<Head title="New Post" />
 
<div>
Form here
<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" />
</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>
</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>
);
}

In the form, we have a submit action where we will add the route later.

Inertia has a form helper that helps with the forms. First, we import the useForm and then use it to define and set the form fields.

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

import AppLayout from '../../Layouts/AppLayout.jsx';
import { Head, Link } from '@inertiajs/react';
import { Head, Link, useForm } from '@inertiajs/react';
 
export default function CreatePost() {
const { data, setData, post, processing, errors, reset } = useForm({
title: '',
content: '',
});
 
return (
// ...
);
}

Now we can see the create form.


Submitting the Form

Before submitting the form in the front-end, let's add the back-end part in the Controller.

app/Http/Controllers/PostController.php:

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

To submit the form in the React component, we can use the post function from the Inertia form helper and send the post by providing the route.

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

import AppLayout from '../../Layouts/AppLayout.jsx';
import { Head, Link, useForm } from '@inertiajs/react';
 
export default function CreatePost() {
const { data, setData, post, processing, errors, reset } = useForm({
title: '',
content: '',
});
 
const submit = (e) => {
e.preventDefault();
 
post(route('posts.store'));
};
 
return (
// ...
);
}

Inertia's beauty is that we made a Laravel redirect after creating the post. Still, Inertia catches the redirect and keeps the SPA feeling by loading only the posts React component without loading all the assets.

Previous: Multiple Layouts: Login Form
avatar

#1 This should come before "Now we can see the create form."

    const submit = (e) => { 
        e.preventDefault();
 
        post(route('posts.store'));
    };

Otherwise console shows submit is not defined.

#2 This I understand would be resolved by anyone who already know Laravel well enough - but just in case : protected $fillable = ['title', 'content']; has to be in app\Models\Post.php otherwise we'll get the error : Add [title] to the fillable property to allow mass assignment on [App\Post].

👍 1
avatar
You can use Markdown
avatar

Hello. Great tutorial. I have a form with fill-gap exercises (sentences and users must complete them). I want to submit a form, then highlight with green the correct ones and highlight the incorrect ones with red. In the example, after storing the post there is a redirection: return redirect()->route('posts.index'); how can I stay in the same form, receive the results, and update the UI? Thanks so much.

avatar

Return as validation errors maybe. Your question isn't something that can be answered here in the comment especially without more context

avatar

Thanks for your replying. I don't expect an error, I want to a proper response and update the UI. I just want to change the redirection in the store method for something that allow me stay in the same page and update it.

avatar

redirect()->back() maybe. But if you know the route to which you want to get back why not use it? As I said it's a bit hard to answer in a comment here

avatar

In your case a manual inertia visit might be even

avatar
You can use Markdown
avatar
You can use Markdown