Courses

React Laravel 12 Starter Kit: CRUD Project

Edit Form and Breadcrumbs

Summary of this lesson:
- Adding "Edit" button to tasks table
- Building task editing form with checkbox for completion status
- Implementing form submission with proper error handling
- Adding breadcrumbs for improved navigation

Now, let's build the page to edit the task. It will be structurally similar to the Create form but with a few differences.

Also, at the end of this lesson, we will add breadcrumbs above the table.


Table: Link to Edit Route

We need to add the "Edit" button to the table.

{tasks.map((task) => (
<TableRow key={task.id}>
 
// ...
 
<TableCell className="flex flex-row gap-x-2 text-right">
 
<Link className={buttonVariants({ variant: 'default' })}
href={`/tasks/${task.id}/edit`}>
Edit
</Link>
 
<Button variant={'destructive'} className={'cursor-pointer'}
onClick={() => deleteTask(task.id)}>
Delete
</Button>
</TableCell>
</TableRow>
))}

Here's the visual result:


Edit Page: React Component

The previous code snippet above shows the URL /tasks/${task.id}/edit. This corresponds to the Controller method edit(), which uses Route Model Binding to find the task.

app/Http/Controllers/TaskController.php

public function edit(Task $task)
{
return Inertia::render('Tasks/Edit', [
'task' => $task,
]);
}

Now, we need to create the Tasks/Edit.tsx React component. I will just show you the full code here. Most of it is identical to the Tasks/Create.tsx component, except for the new is_completed checkbox.

resources/js/pages/Tasks/Edit.jsx:

import InputError from '@/components/input-error';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import AppLayout from '@/layouts/app-layout';
import { type Task } from '@/types';
import { Head, useForm } from '@inertiajs/react';
import { FormEventHandler, useRef } from 'react';
 
type EditTaskForm = {
name: string;
is_completed: boolean;
};
 
export default function Edit({ task }: { task: Task }) {
const taskName = useRef<HTMLInputElement>(null);
 
const { data, setData, errors, put, reset, processing } = useForm<Required<EditTaskForm>>({
name: task.name,
is_completed: task.is_completed,
});
 
const editTask: FormEventHandler = (e) => {
e.preventDefault();
 
put(route('tasks.update', task.id), {
preserveScroll: true,
onSuccess: () => {
reset();
},
onError: (errors) => {
if (errors.name) {
reset('name');
taskName.current?.focus();
}
},
});
};
return (
<AppLayout>
<Head title="Edit Task" />
<div className="flex h-full flex-1 flex-col gap-4 rounded-xl p-4">
<form onSubmit={editTask} className="space-y-6">
<div className="grid gap-2">
<Label htmlFor="name">Task Name</Label>
 
<Input
id="name"
ref={taskName}
value={data.name}
onChange={(e) => setData('name', e.target.value)}
className="mt-1 block w-full"
/>
 
<InputError message={errors.name} />
</div>
<div className="grid gap-2">
<Label htmlFor="is_completed">Completed?</Label>
 
<Checkbox checked={data.is_completed} onCheckedChange={() => setData('is_completed', !data.is_completed)} />
 
<InputError message={errors.is_completed} />
</div>
 
<div className="flex items-center gap-4">
<Button disabled={processing}>Update Task</Button>
</div>
</form>
</div>
</AppLayout>
);
}

As I mentioned, the differences are:

  • New <Checkbox> component from the same Shadcn
  • Different method name editTask and route route('tasks.update', task.id) to update the task

Here's the visual result:

That's it. We have built the full CRUD!


Quick "Bonus" 1: Switch Checkbox

Instead of a regular checkbox, you may want to choose the Shadcn component called Switch.

This would be the visual result:

To achieve this, just install the component:

npx shadcn@latest add switch

And in the Edit.tsx file, all you need to do is change the Checkbox to Switch in two places: the import and the usage. All the parameters are identical.

resources/js/pages/Tasks/Edit.tsx:

import { Checkbox } from '@/components/ui/checkbox';
import { Switch } from '@/components/ui/switch';
 
// ...
 
<Checkbox checked={data.is_completed} onCheckedChange={() => setData('is_completed', !data.is_completed)} />
<Switch checked={data.is_completed} onCheckedChange={() => setData('is_completed', !data.is_completed)} />

Quick "Bonus" 2: Breadcrumbs

To make UX a bit better, we will add the path breadcrumbs on top:

It's very easy in the Laravel starter kit, so we will do it on all three pages: Tasks Index, Create, and Edit.

There's a TypeScript type BreadcrumbItem already defined in the types file:

resources/js/types/index.d.ts:

export interface BreadcrumbItem {
title: string;
href: string;
}

So, we import it into our page components.

Then, we define our Breadcrumb items with their paths.

And the final step is just to pass the breadcrumbs as a parameter to the main <AppLayout> component.

resources/js/pages/Tasks/Index.tsx:

import { type Task } from '@/types';
import { type BreadcrumbItem, type Task } from '@/types';
 
// ...
 
const breadcrumbs: BreadcrumbItem[] = [
{ title: 'Dashboard', href: '/dashboard' },
{ title: 'Tasks', href: '/tasks' },
];
 
export default function Index({ tasks }: { tasks: Task[] }) {
 
// ...
 
return (
<AppLayout breadcrumbs={breadcrumbs}>
<Head title="Tasks List" />

And that's it!

We repeat (almost) the same in the other two pages.

resources/js/pages/Tasks/Create.tsx:

import { type BreadcrumbItem } from '@/types';
 
// ...
 
const breadcrumbs: BreadcrumbItem[] = [
{ title: 'Dashboard', href: '/dashboard' },
{ title: 'Tasks', href: '/tasks' },
{ title: 'Create', href: '/tasks' },
];
 
export default function Create() {
 
// ...
 
return (
<AppLayout breadcrumbs={breadcrumbs}>
<Head title="Create Task" />

resources/js/pages/Tasks/Edit.tsx:

import { type Task } from '@/types';
import { type BreadcrumbItem, type Task } from '@/types';
 
// ...
 
const breadcrumbs: BreadcrumbItem[] = [
{ title: 'Dashboard', href: '/dashboard' },
{ title: 'Tasks', href: '/tasks' },
{ title: 'Edit', href: '' },
];
 
export default function Edit({ task }: { task: Task }) {
 
// ...
 
return (
<AppLayout breadcrumbs={breadcrumbs}>
<Head title="Edit Task" />

So yeah, this is how easy it is to add breadcrumbs to the React starter kit.

Here are the GitHub commits for the Create and Edit forms and for the Breadcrumbs.


That's it for this section: we've created a simple CRUD.

In the next lessons, we will add more elements to that CRUD:

  • Pagination
  • More fields/components: Date Picker, File Upload, Select
  • Add widgets on Dashboard
  • ... and more
Previous: Create Form with Inertia and TypeScript
avatar

Hi,

Just a small typo in the tutorial. In the breadcrumbs section for create:

const breadcrumbs: BreadcrumbItem[] = [
    { title: 'Dashboard', href: '/dashboard' },
    { title: 'Tasks', href: '/tasks' },
    { title: 'Create', href: '/tasks' },
];

The href for 'Create' should be to an empty string { title: 'Create', href: ' ' },

avatar
You can use Markdown
avatar
You can use Markdown