Update Task Status and Delete Tasks
Now let’s wire up the move and delete actions in TaskCard to the backend.
Update TaskCard
Update src/components/task-card.tsx to wire up the move and delete actions:
import { MoreHorizontal, Trash2 } from "lucide-react";
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useMutation } from "convex/react";
import { api } from "../../convex/_generated/api";
import type { Doc } from "../../convex/_generated/dataModel";
type TaskCardProps = {
task: Doc<"tasks">;
};
function TaskCard({ task }: TaskCardProps) {
const updateStatus = useMutation(api.tasks.updateStatus);
const removeTask = useMutation(api.tasks.remove);
return (
<Card className="transition-shadow hover:shadow-md">
<CardHeader className="flex flex-row items-start justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium leading-snug">
{task.title}
</CardTitle>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="h-7 w-7 shrink-0">
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{task.status !== "todo" && (
<DropdownMenuItem
onClick={() =>
updateStatus({ id: task._id, status: "todo" })
}
>
Move to To Do
</DropdownMenuItem>
)}
{task.status !== "in-progress" && (
<DropdownMenuItem
onClick={() =>
updateStatus({ id: task._id, status: "in-progress" })
}
>
Move to In Progress
</DropdownMenuItem>
)}
{task.status !== "done" && (
<DropdownMenuItem
onClick={() =>
updateStatus({ id: task._id, status: "done" })
}
>
Move to Done
</DropdownMenuItem>
)}
<DropdownMenuSeparator />
<DropdownMenuItem
className="text-destructive"
onClick={() => removeTask({ id: task._id })}
>
<Trash2 className="mr-2 h-4 w-4" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</CardHeader>
{task.description && (
<CardContent>
<p className="line-clamp-2 text-sm text-muted-foreground">
{task.description}
</p>
</CardContent>
)}
</Card>
);
}
export default TaskCard;
We call useMutation twice here, once to get the updateStatus function and once to get the remove function. When the user clicks a menu item, we call one of those functions with the arguments it needs. Both actions identify the task by its _id, which is the Convex document ID. The updateStatus mutation takes { id, status } and the remove mutation takes { id }.
Try It Out
The app is now fully wired to Convex:
- Click the
⋯menu and move a task — it changes columns instantly. - Delete a task — it disappears.

Every action goes through the server. There is no local state involved anymore (except for the form inputs in the dialog).
Checkpoint: Commit your progress.
git add .
git commit -m "kanban-09: Wire up useMutation to move or delete tasks"
git push