How To Develop A CRUD App with Laravel 9 and React

How To Develop A CRUD App with Laravel 9 and React

Avatar photoPosted by

Hi there, today we will be developing a CRUD app in Laravel 9 and React. This tutorial will give you an idea how to start building your app if you plan to build it on Laravel and React. Lets have a little bit of discussions.

Laravel is a free, open-source PHP Web Framework and intended for the development of web applications following the MVC (Model-View-Controller) architectural pattern. It is designed to make developing web apps faster and easier by using the built-in features.

React or also called React.js or Reactjs is a free and open-source JavaScript library used for building user interfaces(UI). It is one of the most popular JavaScript library for building front-end. React is created by Facebook and maintained by Facebook.

Before you proceed , you must build first a REST API. Follow this tutorial – How To Make Laravel 9 REST API, before proceeding on the steps below.

Step 1: Setup Front-end Scaffolding

First let’s install the laravel/ui package:

composer require laravel/ui

Then we can now install the react front-end scaffolding

php artisan ui react
npm install

Step 2: Install Some Dependencies

We will then install react-router-dom, this will be used for the routing system of our app.

npm install react-router-dom

And then we will install sweetalert to have a beautiful pop-up boxes

npm install sweetalert2

Step 3: Create View File

After installing the necessary packages, we will then create a view file that will be used for our react application. Create a file in /resources/views/app.blade.php and add these codes:

/resources/views/app.blade.php

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Project Manager</title>
        <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    </head>
    <body>
        <div id="app"></div>
        <script src="{{ asset('js/app.js') }}"></script>
    </body>
</html>

Step 4: Register A Route

After create the view file, we will register a route in /routes/web.php. Remove the default route on the file and register this route.

/routes/web.php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| 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('{any}', function () {
    return view('app'); 
})->where('any', '.*');

Step 5: Run the Application

To test if our app is running:

Execute this command to run our Laravel App:

php artisan serve

And then run this command to watch file changes:

npm run watch

Open this URL to check if we have no errors.

http://localhost:8000/

Step 6: Create The React Files

We will now start creating our react files. We will create these files inside /resources/js directory. These will be what the file structure looks:

How To Develop A CRUD App with Laravel 8 and React

You can have your own way of managing or structuring you files.

Let’s create the Main.js file – This file we be the one that handles the routing:

resources/js/Main.js

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import ProjectList from "./pages/ProjectList"
import ProjectCreate from "./pages/ProjectCreate"
import ProjectEdit from "./pages/ProjectEdit"
import ProjectShow from "./pages/ProjectShow"
 
function Main() {
    return (
        <Router>
            <Routes>
                <Route exact path="/"  element={<ProjectList/>} />
                <Route path="/create"  element={<ProjectCreate/>} />
                <Route path="/edit/:id"  element={<ProjectEdit/>} />
                <Route path="/show/:id"  element={<ProjectShow/>} />
            </Routes>
        </Router>
    );
}
 
export default Main;
 
if (document.getElementById('app')) {
    ReactDOM.render(<Main />, document.getElementById('app'));
}

After creating the Main.js file, let’s update the app.js file:

resources/js/app.js

/**
 * First we will load all of this project's JavaScript dependencies which
 * includes React and other helpers. It's a great starting point while
 * building robust, powerful web applications using React + Laravel.
 */

require('./bootstrap');

/**
 * Next, we will create a fresh React component instance and attach it to
 * the page. Then, you may begin adding components to this application
 * or customize the JavaScript scaffolding to fit your unique needs.
 */

require('./Main');

Inside the /resources/js/components folder, let’s create a file named Layout.js – this will serve as a template. And also you can delete the Example.js file.

resources/js/components/Layout.js

import React from 'react';

const Layout =({children}) =>{
    return(
        <div className="container">{children}</div>
    )
}

export default Layout;

We will now create a folder in /resources/js named pages. Inside the folder let’s create these files for our CRUD operations:

  • ProjectCreate.js
  • ProjectEdit.js
  • ProjectList.js
  • ProjectShow.js

/resources/js/pages/ProjectCreate.js

import React, {useState} from 'react';
import { Link } from "react-router-dom";
import Layout from "../components/Layout"
import Swal from 'sweetalert2'

function ProjecCreate() {
    const [name, setName] = useState('');
    const [description, setDescription] = useState('')
    const [isSaving, setIsSaving] = useState(false)

    const handleSave = () => {
        setIsSaving(true);
        axios.post('/api/projects', {
            name: name,
            description: description
          })
          .then(function (response) {
            Swal.fire({
                icon: 'success',
                title: 'Project saved successfully!',
                showConfirmButton: false,
                timer: 1500
            })
            setIsSaving(false);
            setName('')
            setDescription('')
          })
          .catch(function (error) {
            Swal.fire({
                icon: 'error',
                title: 'An Error Occured!',
                showConfirmButton: false,
                timer: 1500
            })
            setIsSaving(false)
          });
    }

    return (
        <Layout>
            <div className="container">
                <h2 className="text-center mt-5 mb-3">Create New Project</h2>
                <div className="card">
                    <div className="card-header">
                        <Link 
                            className="btn btn-outline-info float-right" 
                            to="/">View All Projects
                        </Link>
                    </div>
                    <div className="card-body">
                        <form>
                            <div className="form-group">
                                <label htmlFor="name">Name</label>
                                <input 
                                    onChange={(event)=>{setName(event.target.value)}}
                                    value={name}
                                    type="text" 
                                    className="form-control" 
                                    id="name" 
                                    name="name"/>
                            </div>
                            <div className="form-group">
                                <label htmlFor="description">Description</label>
                                <textarea 
                                    value={description}
                                    onChange={(event)=>{setDescription(event.target.value)}}
                                    className="form-control" 
                                    id="description" 
                                    rows="3" 
                                    name="description"></textarea>
                            </div>
                            <button 
                                disabled={isSaving}
                                onClick={handleSave} 
                                type="button" 
                                className="btn btn-outline-primary mt-3">
                                Save Project
                            </button>
                        </form>
                    </div>
                </div>
            </div>
        </Layout>
    );
}

export default ProjecCreate;

/resources/js/pages/ProjectEdit.js

import React, { useState, useEffect } from 'react';
import { Link, useParams } from "react-router-dom";
import Layout from "../components/Layout"
import Swal from 'sweetalert2'

function ProjectEdit() {
    const [id, setId] = useState(useParams().id)
    const [name, setName] = useState('');
    const [description, setDescription] = useState('')
    const [isSaving, setIsSaving] = useState(false)

    
    useEffect(() => {
        axios.get(`/api/projects/${id}`)
        .then(function (response) {
            let project = response.data
            setName(project.name);
            setDescription(project.description);
        })
        .catch(function (error) {
            Swal.fire({
                 icon: 'error',
                title: 'An Error Occured!',
                showConfirmButton: false,
                timer: 1500
            })
        })
        
    }, [])


    const handleSave = () => {
        setIsSaving(true);
        axios.patch(`/api/projects/${id}`, {
            name: name,
            description: description
        })
        .then(function (response) {
            Swal.fire({
                icon: 'success',
                title: 'Project updated successfully!',
                showConfirmButton: false,
                timer: 1500
            })
            setIsSaving(false);
        })
        .catch(function (error) {
            Swal.fire({
                 icon: 'error',
                title: 'An Error Occured!',
                showConfirmButton: false,
                timer: 1500
            })
            setIsSaving(false)
        });
    }


    return (
        <Layout>
            <div className="container">
                <h2 className="text-center mt-5 mb-3">Edit Project</h2>
                <div className="card">
                    <div className="card-header">
                        <Link 
                            className="btn btn-outline-info float-right" 
                            to="/">View All Projects
                        </Link>
                    </div>
                    <div className="card-body">
                        <form>
                            <div className="form-group">
                                <label htmlFor="name">Name</label>
                                <input 
                                    onChange={(event)=>{setName(event.target.value)}}
                                    value={name}
                                    type="text" 
                                    className="form-control" 
                                    id="name" 
                                    name="name"/>
                            </div>
                            <div className="form-group">
                                <label htmlFor="description">Description</label>
                                <textarea 
                                    value={description}
                                    onChange={(event)=>{setDescription(event.target.value)}}
                                    className="form-control" 
                                    id="description" 
                                    rows="3" 
                                    name="description"></textarea>
                            </div>
                            <button 
                                disabled={isSaving}
                                onClick={handleSave} 
                                type="button" 
                                className="btn btn-outline-success mt-3">
                                Update Project
                            </button>
                        </form>
                    </div>
                </div>
            </div>
        </Layout>
    );
}

export default ProjectEdit;

/resources/js/pages/ProjectList.js

import React,{ useState, useEffect} from 'react';
import { Link } from "react-router-dom";
import Layout from "../components/Layout"
import Swal from 'sweetalert2'

function ProjectList() {
    const  [projectList, setProjectList] = useState([])

    useEffect(() => {
        fetchProjectList()
    }, [])

    const fetchProjectList = () => {
        axios.get('/api/projects')
        .then(function (response) {
          setProjectList(response.data);
        })
        .catch(function (error) {
          console.log(error);
        })
    }

    const handleDelete = (id) => {
        Swal.fire({
            title: 'Are you sure?',
            text: "You won't be able to revert this!",
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: 'Yes, delete it!'
          }).then((result) => {
            if (result.isConfirmed) {
                axios.delete(`/api/projects/${id}`)
                .then(function (response) {
                    Swal.fire({
                        icon: 'success',
                        title: 'Project deleted successfully!',
                        showConfirmButton: false,
                        timer: 1500
                    })
                    fetchProjectList()
                })
                .catch(function (error) {
                    Swal.fire({
                         icon: 'error',
                        title: 'An Error Occured!',
                        showConfirmButton: false,
                        timer: 1500
                    })
                });
            }
          })
    }

    return (
        <Layout>
           <div className="container">
            <h2 className="text-center mt-5 mb-3">Laravel Project Manager</h2>
                <div className="card">
                    <div className="card-header">
                        <Link 
                            className="btn btn-outline-primary" 
                            to="/create">Create New Project
                        </Link>
                    </div>
                    <div className="card-body">
            
                        <table className="table table-bordered">
                            <thead>
                                <tr>
                                    <th>Name</th>
                                    <th>Description</th>
                                    <th width="240px">Action</th>
                                </tr>
                            </thead>
                            <tbody>
                                {projectList.map((project, key)=>{
                                    return (
                                        <tr key={key}>
                                            <td>{project.name}</td>
                                            <td>{project.description}</td>
                                            <td>
                                                <Link
                                                    to={`/show/${project.id}`}
                                                    className="btn btn-outline-info mx-1">
                                                    Show
                                                </Link>
                                                <Link
                                                    className="btn btn-outline-success mx-1"
                                                    to={`/edit/${project.id}`}>
                                                    Edit
                                                </Link>
                                                <button 
                                                    onClick={()=>handleDelete(project.id)}
                                                    className="btn btn-outline-danger mx-1">
                                                    Delete
                                                </button>
                                            </td>
                                        </tr>
                                    )
                                })}
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </Layout>
    );
}

export default ProjectList;

/resources/js/pages/ProjectShow.js

import React, {useState, useEffect} from 'react';
import { Link, useParams } from "react-router-dom";
import Layout from "../components/Layout"

function ProjectShow() {
    const [id, setId] = useState(useParams().id)
    const [project, setProject] = useState({name:'', description:''})
    useEffect(() => {
        axios.get(`/api/projects/${id}`)
        .then(function (response) {
          setProject(response.data)
        })
        .catch(function (error) {
          console.log(error);
        })
    }, [])

    return (
        <Layout>
           <div className="container">
            <h2 className="text-center mt-5 mb-3">Show Project</h2>
                <div className="card">
                    <div className="card-header">
                        <Link 
                            className="btn btn-outline-info float-right" 
                            to="/"> View All Projects
                        </Link>
                    </div>
                    <div className="card-body">
                        <b className="text-muted">Name:</b>
                        <p>{project.name}</p>
                        <b className="text-muted">Description:</b>
                        <p>{project.description}</p>
                    </div>
                </div>
            </div>
        </Layout>
    );
}

export default ProjectShow;

We’re all done, what is left is to test our app. Open this URL and test the app:

http://localhost:8000/

Screenshots:

Index Page

laravel-8-react-crud-create-image

Create Page

laravel-8-react-crud-create-image

Edit Page

laravel-8-react-crud-show-image

Show Page

laravel-8-react-crud-show-image