In this tutorial, we will create CRUD application using Laravel Inertia Js with reactjs. Inertia JS is use for create SPA.
Step 1: Install Laravel & Connect Database
Step 2: Install Breeze & Setup Inertia Js React
Step 3: Create Post Model and Resource Controller
Step 4: Create Post Request
Step 5: Create React js view file for CRUD
Step 1: Install Laravel & Connect Database
Installing a fresh new laravel application.
composer create-project laravel/laravel inertia-react
Now, you have to connect the laravel app to the database, hence open the .env configuration file and add the database credentials as suggested below.
.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=database_name
DB_USERNAME=database_user_name
DB_PASSWORD=database_password
Step 2: Install Breeze & Setup Inertia Js React
Install laravel breeze via composer
composer require laravel/breeze --dev
Next, run below command
php artisan breeze:install
install breeze with reactjs
php artisan breeze:install react
And final install Dependencies
npm install && npm run dev
Step 3: Create Post Model and Resource Controller
Run below command in terminal to create post model, migration and controller.
php artisan make:model Post -mcr
app/database/migrations/posts_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePostsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('posts');
}
}
app/Models/Post.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = [
'title',
'description'
];
use HasFactory;
}
Step 4: Create Post Request
Create Post Request for validation
php artisan make:request StorePostRequest
app/Http/Requests/StorePostRequest.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePostRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'title' => 'required',
'description' => 'required'
];
}
}
PostController.php
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StorePostRequest;
use App\Models\Post;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Request;
use Inertia\Inertia;
class PostController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$posts = Post::latest()->get();
return Inertia::render('Post/Index', ['posts' => $posts]);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return Inertia::render('Post/Create');
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(StorePostRequest $request)
{
Post::create(
$request->validated()
);
return Redirect::route('posts.index');
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit(Post $post)
{
return Inertia::render('Post/Edit', [
'post' => [
'id' => $post->id,
'title' => $post->title,
'description' => $post->description
]
]);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(StorePostRequest $request, Post $post)
{
$post->update($request->validated());
return Redirect::route('posts.index');
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy(Post $post)
{
$post->delete();
return Redirect::route('posts.index');
}
}
app/routes/web.php
<?php
use App\Http\Controllers\PostController;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return Inertia::render('Welcome', [
'canLogin' => Route::has('login'),
'canRegister' => Route::has('register'),
'laravelVersion' => Application::VERSION,
'phpVersion' => PHP_VERSION,
]);
});
Route::get('/dashboard', function () {
return Inertia::render('Dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
Route::resource('posts', PostController::class);
require __DIR__.'/auth.php';
Step 5: Create React js view file for CRUD
app/resources/js/Pages/Post/Create.js
import React from "react";
import { Inertia } from "@inertiajs/inertia";
import { InertiaLink, useForm } from "@inertiajs/inertia-react";
const Create = () => {
const { data, setData, errors, post } = useForm({
title: "",
description: "",
});
function handleSubmit(e) {
e.preventDefault();
post(route("posts.store"));
}
return (
<div className="mt-20">
<div className="container flex flex-col justify-center mx-auto">
<div>
<h1 className="mb-8 text-3xl font-bold">
<InertiaLink
href={route("posts.index")}
className="text-indigo-600 hover:text-indigo-700"
>
Posts
</InertiaLink>
<span className="font-medium text-indigo-600"> / </span>
Create
</h1>
</div>
<div className="max-w-6xl p-8 bg-white rounded shadow">
<form name="createForm" onSubmit={handleSubmit}>
<div className="flex flex-col">
<div className="mb-4">
<label className="">Title</label>
<input
type="text"
className="w-full px-4 py-2"
label="Title"
name="title"
value={data.title}
onChange={(e) =>
setData("title", e.target.value)
}
/>
<span className="text-red-600">
{errors.title}
</span>
</div>
<div className="mb-0">
<label className="">Description</label>
<textarea
type="text"
className="w-full rounded"
label="description"
name="description"
errors={errors.description}
value={data.description}
onChange={(e) =>
setData("description", e.target.value)
}
/>
<span className="text-red-600">
{errors.description}
</span>
</div>
</div>
<div className="mt-4">
<button
type="submit"
className="px-6 py-2 font-bold text-white bg-green-500 rounded"
>
Save
</button>
</div>
</form>
</div>
</div>
</div>
);
};
export default Create;
app/resources/js/Pages/Post/Index.js
import React from "react";
import { Inertia } from "@inertiajs/inertia";
import { InertiaLink, usePage } from "@inertiajs/inertia-react";
const Index = () => {
const { posts } = usePage().props;
const { data } = posts;
return (
<div>
<div className="container mx-auto">
<h1 className="mb-8 text-3xl font-bold text-center">Post</h1>
<div className="flex items-center justify-between mb-6">
<InertiaLink
className="px-6 py-2 text-white bg-green-500 rounded-md focus:outline-none"
href={route("posts.create")}
>
Create Post
</InertiaLink>
</div>
<div className="overflow-x-auto bg-white rounded shadow">
<table className="w-full whitespace-nowrap">
<thead className="text-white bg-gray-600">
<tr className="font-bold text-left">
<th className="px-6 pt-5 pb-4">#</th>
<th className="px-6 pt-5 pb-4">Title</th>
<th className="px-6 pt-5 pb-4">Description</th>
<th className="px-6 pt-5 pb-4">Action</th>
</tr>
</thead>
<tbody>
{data.map(({ id, title, description }) => (
<tr key={id} className="">
<td className="border-t">
<InertiaLink
href={route("posts.edit", id)}
className="flex items-center px-6 py-4 focus:text-indigo-700 focus:outline-none"
>
{id}
</InertiaLink>
</td>
<td className="border-t">
<InertiaLink
href={route("posts.edit", id)}
className="flex items-center px-6 py-4 focus:text-indigo-700 focus:outline-none"
>
{title}
</InertiaLink>
</td>
<td className="border-t">
<InertiaLink
tabIndex="1"
className="flex items-center px-6 py-4"
href={route("posts.edit", id)}
>
{description}
</InertiaLink>
</td>
<td className="border-t">
<InertiaLink
tabIndex="1"
className="px-4 py-2 text-sm text-white bg-blue-500 rounded"
href={route("posts.edit", id)}
>
Edit
</InertiaLink>
</td>
</tr>
))}
{data.length === 0 && (
<tr>
<td
className="px-6 py-4 border-t"
colSpan="4"
>
No contacts found.
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
</div>
);
};
export default Index;
app/resources/js/Pages/Post/Edit.js
import React from "react";
import { Inertia } from "@inertiajs/inertia";
import { InertiaLink, usePage, useForm } from "@inertiajs/inertia-react";
const Edit = () => {
const { post } = usePage().props;
const { data, setData, put, errors } = useForm({
title: post.title || "",
description: post.description || "",
});
function handleSubmit(e) {
e.preventDefault();
put(route("posts.update", post.id));
}
function destroy() {
if (confirm("Are you sure you want to delete this user?")) {
Inertia.delete(route("posts.destroy", post.id));
}
}
return (
<div className="mt-20">
<div className="container flex flex-col justify-center mx-auto">
<div>
<h1 className="mb-8 text-3xl font-bold">
<InertiaLink
href={route("posts.index")}
className="text-indigo-600 hover:text-indigo-700"
>
Posts
</InertiaLink>
<span className="font-medium text-indigo-600"> /</span>
Edit
</h1>
</div>
<div className="max-w-3xl p-8 bg-white rounded shadow">
<form name="createForm" onSubmit={handleSubmit}>
<div className="flex flex-col">
<div className="mb-4">
<label className="">Title</label>
<input
type="text"
className="w-full px-4 py-2"
label="Title"
name="title"
value={data.title}
onChange={(e) =>
setData("title", e.target.value)
}
/>
<span className="text-red-600">
{errors.title}
</span>
</div>
<div className="mb-4">
<label className="">Description</label>
<textarea
type="text"
className="w-full rounded"
label="description"
name="description"
errors={errors.description}
value={data.description}
onChange={(e) =>
setData("description", e.target.value)
}
/>
<span className="text-red-600">
{errors.description}
</span>
</div>
</div>
<div className="flex justify-between">
<button
type="submit"
className="px-4 py-2 text-white bg-green-500 rounded"
>
Update
</button>
<button
onClick={destroy}
tabIndex="-1"
type="button"
className="px-4 py-2 text-white bg-red-500 rounded"
>
Delete
</button>
</div>
</form>
</div>
</div>
</div>
);
};
export default Edit;
Note: Always try to open to two terminal window one use for server and second for below command.
npm run watch