Copied!
Laravel
Laravel 10 and React JS CRUD Tutorial: Building a Full-Stack App with Vite (Step-by-Step Guide)
laravel-and-react-crud.jpg
Shahroz Javed
Sep 02, 2023 . 325 views

Table Of Contents

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:

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.

13 Shares

Related Posts

Similar Posts