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!
Transform from beginner to Laravel expert with our personalized Coaching Class starting June 23, 2025. Limited enrollment ensures focused attention.
1-hour personalized coaching
Build portfolio applications
Industry-standard techniques
Interview prep & job guidance
Complete your application to secure your spot
Thank you for your interest in our Laravel mentorship program. We'll contact you within 24 hours with next steps.
No comments yet. Be the first to comment!
Please log in to post a comment:
Sign in with Google