Contents
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:
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
Create Page
Edit Page
Show Page