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 routeroute('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
Hi,
Just a small typo in the tutorial. In the breadcrumbs section for create:
The href for 'Create' should be to an empty string
{ title: 'Create', href: ' ' },