# useDelete (https://v1.s3.dimah.dev/docs/react/hooks/delete)

`useDelete` coordinates object deletion with your server API. It supports
two-step confirmation, immediate deletion, and batch operations.

```tsx title="components/delete-avatar.tsx"
"use client";

import { useDelete } from "@dimah-s3/react";

export function DeleteAvatar({ avatarKey }: { avatarKey: string }) {
  const {
    isConfirming,
    requestDelete,
    confirmDelete,
    cancelDelete,
    isDeleting,
  } = useDelete({
    route: "avatar",
    onSuccess: (key) => {
      console.log("Deleted avatar key:", key);
    },
  });

  if (isConfirming) {
    return (
      <div className="flex items-center gap-2">
        <span className="text-sm">Delete avatar?</span>
        <button
          type="button"
          onClick={() => void confirmDelete()}
          disabled={isDeleting}
          className="text-destructive font-medium"
        >
          {isDeleting ? "Deleting…" : "Confirm"}
        </button>
        <button type="button" onClick={cancelDelete}>
          Cancel
        </button>
      </div>
    );
  }

  return (
    <button type="button" onClick={() => requestDelete(avatarKey)}>
      Delete Avatar
    </button>
  );
}
```

---

## Deletion workflows [#deletion-workflows]

### 1. Two-step confirmation flow [#1-two-step-confirmation-flow]

Use `requestDelete(key)` to enter the `confirming` state for that specific key. This keeps the pending key in state until the user approves (`confirmDelete()`) or cancels (`cancelDelete()`).

```tsx
const { isConfirming, requestDelete, confirmDelete, cancelDelete } = useDelete({
  route: "avatar",
});
```

### 2. Immediate deletion (`remove`) [#2-immediate-deletion-remove]

If your UI manages its own modal dialog or requires direct execution without internal confirmation state, call `remove(key)`:

```tsx
const { remove, isDeleting } = useDelete({ route: "avatar" });

await remove(avatarKey);
```

### 3. Batch deletion (`removeMany`) [#3-batch-deletion-removemany]

Delete multiple keys in one dimah-s3 API request. The server authorizes and
deletes each object independently, so the result can contain partial failures:

```tsx
const { removeMany } = useDelete({ route: "avatar" });

await removeMany([key1, key2, key3]);
```

---

## Type reference [#type-reference]

```ts
import type { UseDeleteOptions, UseDeleteReturn } from "@dimah-s3/react";
```

### UseDeleteOptions [#usedeleteoptions]

<AutoTypeTable
  path="packages/react/src/hooks/use-delete.ts"
  name="UseDeleteOptions"
/>

---

### UseDeleteReturn [#usedeletereturn]

<AutoTypeTable
  path="packages/react/src/hooks/use-delete.ts"
  name="UseDeleteReturn"
/>

## Frequently asked questions [#frequently-asked-questions]

<Accordions>

<Accordion title="How do I ensure only the object owner can delete it?">

Enforce ownership on the server in `delete.guard`; client-side checks are not
an authorization boundary. The default `keyPrefix` only keeps keys inside this
route's folder — it does not know which user owns a file. See
[Security](https://v1.s3.dimah.dev/docs/server/security#route-namespace-vs-ownership).

```ts title="lib/s3.ts"
avatar: route({
  delete: {
    guard: async ({ key, request }) => {
      const session = await getSession(request);
      if (!session) throw errors.unauthorized();

      const isOwner = await checkOwnership(session.userId, key);
      if (!isOwner) throw errors.forbidden();
    },
    onDeleted: async ({ key, request }) => {
      // Database record cleanup
    },
  },
});
```

</Accordion>

<Accordion title="How do I clear error states after a failed deletion?">

Call `reset()` to return the hook back to the `idle` phase:

```tsx
const { reset, error } = useDelete({ route: "avatar" });

if (error) {
  return (
    <div>
      <p className="text-destructive">{error.message}</p>
      <button type="button" onClick={reset}>
        Try Again
      </button>
    </div>
  );
}
```

</Accordion>

</Accordions>
