Introduction
This tutorial demonstrates the integration of React into Laravel using Vite, streamlining the development of dynamic user interfaces. We'll also guide you through implementing CRUD operations, enabling efficient data management. You'll gain a comprehensive understanding of how React, Laravel, and Vite can collaborate to create sophisticated web applications with powerful CRUD functionalities.
2: Integrate React in Laravel using Vite:
In this tutorial we will be using vite as frontend tooling.
Requirements:
- Laravel 9 or above
- node 14 or above
2.1: create Laravel project
composer create-project --prefer-dist laravel/laravel LaravelReact
2.2: Install react and vite react-plugin
npm i
npm install react@latest react-dom@latest
npm i @vitejs/plugin-react
2.3: Update vite.config.js
import { defineConfig } from "vite";
import laravel from "laravel-vite-plugin";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [
laravel({
input: ["resources/js/app.jsx"],
refresh: true,
}),
react(),
],
});
2.4: Create a dynamic route
In web.php add this route. this route will handle all routes that we will make using react routing
Route::get('/{any}', function () {
return view('app');
})->where('any', '.*');
2.5: Update Layout blade file i.e (app.blade.php)
add vite directives and div with id app here is an example.
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel 9 vite with react</title>
@viteReactRefresh
@vite('resources/js/app.jsx')
</head>
<body>
<div id="app"></div>
</body>
</html>
2.6: Install Dependencies
install Following Packages
npm i bootstrap jquery toastr react-router-dom
2.7: Update resources/js/app.jsx
import "bootstrap/dist/css/bootstrap.min.css";
import "toastr/build/toastr.css";
import ReactDOM from "react-dom/client";
import App from "./Pages/App";
ReactDOM.createRoot(document.getElementById("app")).render(<App />);
We will use bootstrap for styling. And toastr is a toast library we will use to show messages when we delete, add, update posts.
2.8: Create new root page i.e [App.jsx]
resources/js/Pages/App.jsx
import React from "react";
const App = () => {
return <div>React Integrated Successfully.</div>;
};
export default App;
2.9: Run following commands
React integration is done here .
npm run dev
php artisan serve
3: Create Model, migration, and controller
php artisan make:model Post -mc
This command will create a Post model, migration for posts table and PostController
//in posts migration
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('description');
$table->timestamps();
});
php artisan migrate
4: Create CRUD API`s
4.1: create routes in routes/api.php
Route::get('/posts', [PostController::class, 'index']);
Route::get('/posts/delete/{id}', [PostController::class, 'delete']);
Route::get('/posts/get/{id}', [PostController::class, 'get']);
Route::post('/posts/save', [PostController::class, 'save']);
Route::post('/posts/update', [PostController::class, 'update']);
4.2: create Api's in PostController
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
function index()
{
return response(['status' => 'success', 'posts' => Post::all(), 'code' => 200]);
}
function save(Request $request)
{
$request->validate(['title' => 'required', 'description' => 'required']);
$post = new Post();
$post->title = $request->title;
$post->description = $request->description;
$post->save();
return response(['status' => 'success', 'post' => $post, 'code' => 200]);
}
// get post by id
function get($id)
{
$post = Post::find($id);
return response(['status' => 'success', 'post' => $post, 'code' => 200]);
}
function update(Request $request)
{
$request->validate(['title' => 'required', 'description' => 'required']);
$post = Post::find($request->id);
$post->title = $request->title;
$post->description = $request->description;
$post->save();
return response(['status' => 'success', 'post' => $post, 'code' => 200]);
}
function delete($id)
{
$post = Post::find($id);
$post->delete();
return response(['status' => 'success', 'message' => 'deleted successfully', 'code' => 200]);
}
}
5: Create pages and and integrate Api's
Create 3 pages in resources/js/Pages/Post. Posts.jsx for post list, CreatePost.jsx for create post and EditPost.jsx for edit post.
5.1 Create routes in App.jsx
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Posts from "./Post/Posts";
import CreatePost from "./Post/CreatePost";
import EditPost from "./Post/EditPost";
const App = () => {
return (
<>
<BrowserRouter>
<Routes>
<Route exact path="/" element={<Posts />}></Route>
<Route
exact
path="/create-post"
element={<CreatePost />}
></Route>
<Route
exact
path="/edit-post/:id"
element={<EditPost />}
></Route>
<Route path="*" element={<h1>404 Not found</h1>}></Route>
</Routes>
</BrowserRouter>
</>
);
};
export default App;
5.2: Posts.jsx page
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import toastr from "toastr";
const Posts = () => {
const [posts, setPosts] = useState([]);
//delete post
async function deletePost(id) {
let response = await axios.get(`http://localhost:8000/api/posts/delete/${id}`);
toastr.success(response.data.message);
getPosts();
}
//fetch all posts
async function getPosts() {
let response = await axios.get("http://localhost:8000/api/posts");
let postItems = response.data.posts;
setPosts(postItems);
}
useEffect(() => {
getPosts();
}, []);
return (
<>
<div className="container mt-2">
<div className="card">
<div className="card-header">
<div className="d-flex justify-content-between align-items-center">
<h1>Posts</h1>
<Link
className="btn btn-secondary"
to="/create-post"
>
Create
</Link>
</div>
</div>
<div className="card-body">
<table
id="myTable"
className="table table-bordered table-hover"
>
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Description</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{posts.map((post, index) => {
return (
<tr key={post.id}>
<td>{index + 1}</td>
<td>{post.title}</td>
<td>{post.description}</td>
<td>
<Link
className="btn btn-info"
to={`edit-post/${post.id}`}
>
Edit
</Link>
<button
className="btn btn-danger ms-1"
onClick={() =>
deletePost(post.id)
}
>
Delete
</button>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
</div>
</>
);
};
export default Posts;
5.3 CreatePost page
import axios from "axios";
import React, { useState } from "react";
import { Link } from "react-router-dom";
import toastr from "toastr";
const CreatePost = () => {
const [post, setPost] = useState({ title: "", description: "" });
function handleInput(e) {
const name = e.target.name;
const value = e.target.value;
setPost({ ...post, [name]: value });
}
async function savePost() {
try {
let data = await axios.post("http://localhost:8000/api/posts/save", post);
setPost({ title: "", description: "" });
toastr.success('Post saved Successfully')
} catch (error) {
let errors = error.response.data.errors
for (let key in errors) {
toastr.error(errors[key])
}
}
}
return (
<>
<div className="container mt-2">
<div className="card">
<div className="card-header">
<div className="d-flex justify-content-between align-items-center">
<h1>Create Post</h1>
<Link className="btn btn-secondary" to="/">
Back
</Link>
</div>
</div>
<div className="card-body">
<form action="">
<div className="form-group">
<label htmlFor="title">Title:</label>
<input
type="text"
name="title"
id="title"
className="form-control"
onChange={handleInput}
value={post.title}
/>
</div>
<div className="form-group">
<label htmlFor="description">
Description:
</label>
<input
type="text"
name="description"
id="description"
className="form-control"
onChange={handleInput}
value={post.description}
/>
</div>
<div className="form-group mt-2">
<button
type="button"
className="btn btn-secondary"
onClick={savePost}
>
Save
</button>
</div>
</form>
</div>
</div>
</div>
</>
);
};
export default CreatePost;
5.4 EditPost Page
import axios from "axios";
import React, { useEffect, useState } from "react";
import { Link, useParams } from "react-router-dom";
import toastr from 'toastr'
const EditPost = () => {
const [post, setPost] = useState({ title: "", description: "" });
let params = useParams()
let id = params.id;
async function getPost() {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
let data = await axios.get(`http://localhost:8000/api/posts/get/${id}`, config)
setPost(data.data.post)
}
useEffect(() => {
getPost();
}, [])
function handleInput(e) {
const name = e.target.name;
const value = e.target.value;
setPost({ ...post, [name]: value });
}
async function updatePost() {
try {
let data = await axios.post("http://localhost:8000/api/posts/update", post);
setPost({ title: "", description: "" });
getPost()
toastr.success('Post updated Successfully')
} catch (error) {
let errors = error.response.data.errors
for (let key in errors) {
toastr.error(errors[key])
}
}
}
return (
<>
<div className="container mt-2">
<div className="card">
<div className="card-header">
<div className="d-flex justify-content-between align-items-center">
<h1>Edit Post</h1>
<Link className="btn btn-secondary" to="/">
Back
</Link>
</div>
</div>
<div className="card-body">
<form action="">
<div className="form-group">
<label htmlFor="title">Title:</label>
<input
type="text"
name="title"
id="title"
className="form-control"
onChange={handleInput}
value={post.title}
/>
</div>
<div className="form-group">
<label htmlFor="description">
Description:
</label>
<input
type="text"
name="description"
id="description"
className="form-control"
onChange={handleInput}
value={post.description}
/>
</div>
<div className="form-group mt-2">
<button
type="button"
className="btn btn-secondary"
onClick={updatePost}
>
Save
</button>
</div>
</form>
</div>
</div>
</div>
</>
);
};
export default EditPost;
Conclusion:
By following all these steps you will be able to setup and use React in Laravel as well you will be able to perform Laravel React CRUD opertaion. Please provide your feedback at comment below.