Kritim Yantra
Feb 27, 2025
If you have the Laravel installer installed, create a new project by running:
laravel new laravelreactcrud
This command creates a fresh Laravel 12 application in a folder named laravelreactcrud
.
To leverage a modern frontend, we’ll choose react starter kit that provides React with InertiaJS.
For our CRUD application, we will manage blog posts. Let’s create the Post
model along with a migration and a resource controller.
php artisan make:model Post -m
This command creates a new Post
model and a migration file for creating the posts
table.
php artisan make:controller PostController --resource
Next, open the generated migration file (located in database/migrations
) and update it to include the necessary fields. For example:
// database/migrations/xxxx_xx_xx_create_posts_table.php
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->timestamps();
});
}
Run the migration to create the database table:
php artisan migrate
Your React files for handling CRUD operations can be placed in your preferred directory (commonly under resources/js/Pages
). Below are the key files and their code.
posts/index.tsx
// posts/index.tsx
import AppLayout from '@/layouts/app-layout';
import { type BreadcrumbItem } from '@/types';
import { Head, Link } from '@inertiajs/react';
const breadcrumbs: BreadcrumbItem[] = [
{
title: 'Posts',
href: '/Posts',
},
];
export default function Posts({ posts }) {
return (
<AppLayout breadcrumbs={breadcrumbs}>
<Head title="Posts" />
<div className="container mx-auto p-4">
<div className="flex justify-between items-center mb-4">
<h1 className="text-2xl font-bold">Blog Posts</h1>
<button className="bg-gray-500 text-white px-4 py-1 rounded hover:bg-gray-600">
<Link href="/posts/create">Create</Link>
</button>
</div>
<div className="overflow-x-auto">
<table className="min-w-full bg-white shadow rounded-lg">
<thead>
<tr className="bg-gray-200">
<th className="py-2 px-4 text-left border-b">Name</th>
<th className="py-2 px-4 text-left border-b">Content</th>
<th className="py-2 px-4 text-left border-b">Actions</th>
</tr>
</thead>
<tbody>
{posts.map((post) => (
<tr className="hover:bg-gray-50" key={post.id}>
<td className="py-2 px-4 border-b">{post.title}</td>
<td className="py-2 px-4 border-b">{post.content}</td>
<td className="py-2 px-4 border-b">
<button className="bg-green-500 text-white px-2 py-1 rounded hover:bg-green-600 mr-2">
<Link href={`/posts/${post.id}/edit`}>Edit</Link>
</button>
<Link
className="bg-red-500 text-white px-2 py-1 rounded hover:bg-red-600"
method="delete"
onClick={(e) => {
if (!confirm('Are you sure?')) {
e.preventDefault();
}
}}
href={route('posts.destroy', post.id)}
>
Delete
</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</AppLayout>
);
}
posts/create.tsx
// posts/create.tsx
import AppLayout from '@/layouts/app-layout';
import { type BreadcrumbItem } from '@/types';
import { Head, useForm, Link } from '@inertiajs/react';
const breadcrumbs: BreadcrumbItem[] = [
{
title: 'Posts',
href: '/Posts',
},
];
export default function Posts() {
const { data, setData, errors, post, reset, processing } = useForm({
title: '',
content: '',
});
function submit(e: React.FormEvent) {
e.preventDefault();
post(route('posts.store'), {
preserveScroll: true,
onSuccess: () => reset(),
});
}
return (
<AppLayout breadcrumbs={breadcrumbs}>
<Head title="Posts" />
<div className="container mx-auto p-4">
<div className="flex justify-between items-center mb-4">
<h1 className="text-2xl font-bold">Blog Posts</h1>
</div>
<div className="bg-white shadow rounded-lg p-4 mb-6">
<h2 className="text-xl font-bold mb-4">Add New Post</h2>
<form onSubmit={submit}>
<div className="mb-4">
<label htmlFor="name" className="block text-gray-700 font-medium mb-1">
Title
</label>
<input
type="text"
id="name"
name="title"
value={data.title}
onChange={(e) => setData('title', e.currentTarget.value)}
placeholder="Post Name"
className="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring focus:ring-blue-200"
/>
</div>
<div className="mb-4">
<label htmlFor="content" className="block text-gray-700 font-medium mb-1">
Content
</label>
<textarea
id="content"
name="content"
value={data.content}
onChange={(e) => setData('content', e.currentTarget.value)}
placeholder="Post Content"
rows={4}
className="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring focus:ring-blue-200"
></textarea>
</div>
<button
type="submit"
disabled={processing}
className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
>
{processing ? 'Saving...' : 'Save'}
</button>
</form>
</div>
</div>
</AppLayout>
);
}
posts/edit.tsx
// posts/edit.tsx
import AppLayout from '@/layouts/app-layout';
import { type BreadcrumbItem } from '@/types';
import { Head, useForm, Link } from '@inertiajs/react';
const breadcrumbs: BreadcrumbItem[] = [
{
title: 'Posts',
href: '/Posts',
},
];
export default function Posts({ post }) {
const { data, setData, errors, put, reset, processing } = useForm({
title: post.title,
content: post.content,
});
function submit(e: React.FormEvent) {
e.preventDefault();
put(route('posts.update', post.id), {
preserveScroll: true,
onSuccess: () => reset(),
});
}
return (
<AppLayout breadcrumbs={breadcrumbs}>
<Head title="Posts" />
<div className="container mx-auto p-4">
<div className="flex justify-between items-center mb-4">
<h1 className="text-2xl font-bold">Blog Posts</h1>
</div>
<div className="bg-white shadow rounded-lg p-4 mb-6">
<h2 className="text-xl font-bold mb-4">Edit Post</h2>
<form onSubmit={submit}>
<div className="mb-4">
<label htmlFor="name" className="block text-gray-700 font-medium mb-1">
Title
</label>
<input
type="text"
id="name"
name="title"
value={data.title}
onChange={(e) => setData('title', e.currentTarget.value)}
placeholder="Post Name"
className="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring focus:ring-blue-200"
/>
</div>
<div className="mb-4">
<label htmlFor="content" className="block text-gray-700 font-medium mb-1">
Content
</label>
<textarea
id="content"
name="content"
value={data.content}
onChange={(e) => setData('content', e.currentTarget.value)}
placeholder="Post Content"
rows={4}
className="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring focus:ring-blue-200"
></textarea>
</div>
<button
type="submit"
disabled={processing}
className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
>
{processing ? 'Updating...' : 'Update'}
</button>
</form>
</div>
</div>
</AppLayout>
);
}
Dashboard.tsx
// Dashboard.tsx
import { Link } from '@inertiajs/react';
import { Button } from '@/components/ui/button';
export default function Dashboard() {
return (
<div className="p-4">
<Button key="posts">
<Link href="/posts">Posts</Link>
</Button>
</div>
);
}
Create a controller to handle all CRUD operations. The PostController.php
file should look like this:
// PostController.php
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
use Inertia\Inertia;
class PostController extends Controller
{
public function index()
{
return Inertia::render('posts/index', [
'posts' => Post::all(),
]);
}
public function create()
{
return Inertia::render('posts/create');
}
public function store(Request $request)
{
Post::create([
'title' => $request->title,
'content' => $request->content,
]);
return redirect()->route('posts.index');
}
public function edit(Post $post)
{
return Inertia::render('posts/edit', [
'post' => $post,
]);
}
public function update(Request $request, Post $post)
{
$post->update([
'title' => $request->title,
'content' => $request->content,
]);
return redirect()->route('posts.index');
}
public function destroy(Post $post)
{
$post->delete();
return redirect()->route('posts.index');
}
}
Routes: Define your resource routes in your routes/web.php
file. For example:
Route::resource('posts', PostController::class);
Serve Your Application: Start your local development server:
php artisan serve
Now, navigate to your application in your browser and you should be able to create, read, update, and delete posts using a smooth React interface powered by InertiaJS and styled with Tailwind CSS.
In this guide, we walked through every step—from creating your Laravel project using the Laravel installer to setting up a React-based CRUD application with InertiaJS. By leveraging Laravel 12’s starter kits and combining them with React, InertiaJS, and Tailwind CSS, you can build a modern, responsive, and user-friendly web application in no time.
Happy coding, and enjoy building your Laravel React CRUD application!
No comments yet. Be the first to comment!
Please log in to post a comment:
Continue with Google