CodingNic

Deleting & Favoriting

Build the Delete Button with Confirmation

Deleting & Favoriting 20 min read

Build the Delete Button with Confirmation

Build the Delete Button with Confirmation

Deleting is destructive, so it needs a browser confirm() dialog before it
fires. confirm() only exists in the browser — it can’t run inside a
Server Component, which executes on the server. This is the one spot in
the app that genuinely needs a Client Component.

Create components/DeleteContactButton.tsx:

tsx
"use client";

import { useTransition } from "react";
import { deleteContactAction } from "@/lib/actions";

export default function DeleteContactButton({ id, name }: { id: string; name: string }) {
  const [isPending, startTransition] = useTransition();

  return (
    <button
      type="button"
      className="btn btn-danger"
      disabled={isPending}
      onClick={() => {
        if (confirm(`Delete ${name} from your contact book? This can't be undone.`)) {
          startTransition(() => {
            deleteContactAction(id);
          });
        }
      }}
    >
      {isPending ? "Deleting…" : "Delete"}
    </button>
  );
}

A few things worth understanding:

  • "use client" at the top of the file tells Next.js this component runs
    in the browser, not the server. It’s needed here specifically because
    of confirm() and the interactive onClick handler — not because
    Server Actions themselves require it. You just saw a Server Action
    called from a plain Server Component form in the last lesson.
  • A Server Action is still just an async function — you can call it
    directly from a Client Component’s event handler, not only from a
    form’s action prop. That’s what’s happening in onClick.
  • useTransition gives you isPending, so the button can show
    “Deleting…” and disable itself while the Server Action is in flight,
    preventing a double-click from firing it twice.

Add it to the contact detail page

Open app/contacts/[id]/page.tsx:

tsx
import DeleteContactButton from "@/components/DeleteContactButton";

// ...inside detail-actions:
<div className="detail-actions">
  <Link href={`/contacts/${contact.id}/edit`} className="btn btn-ghost">Edit</Link>
  <Link href="/" className="btn btn-ghost">Back to all contacts</Link>
  <DeleteContactButton id={contact.id} name={contact.name} />
</div>

Test it

Open a contact’s detail page and click Delete. You should get a browser
confirmation dialog with that contact’s name in it. Cancel it — nothing
should happen. Click Delete again and confirm — you should be redirected
to the Home page, and that contact should be gone from the list.

Checkpoint

You can delete a contact after confirming, and you can now point to the
exact line ("use client") that separates this component from every other
one in the app so far, and explain why it’s there.