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:
1 | composer require laravel /ui |
Then we can now install the react front-end scaffolding
1 2 | 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.
1 | npm install react-router-dom |
And then we will install sweetalert to have a beautiful pop-up boxes
1 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <!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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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:
1 | php artisan serve |
And then run this command to watch file changes:
1 | npm run watch |
Open this URL to check if we have no errors.
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /** * 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
1 2 3 4 5 6 7 8 9 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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:
Screenshots:
Index Page

Create Page

Edit Page

Show Page
