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



`useDelete` coordinates object deletion with your server API, featuring built-in support for confirmation dialog flows, single-step 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]

<div className="fd-steps">
  <div className="fd-step">
    ### 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",
    });
    ```
  </div>

  <div className="fd-step">
    ### 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);
    ```
  </div>

  <div className="fd-step">
    ### Batch deletion (`removeMany`) [#3-batch-deletion-removemany]

    Delete multiple keys in a single request via S3 `DeleteObjects`:

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

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

    ***
  </div>
</div>

## Type reference [#type-reference]

```ts
import type {
  UseDeleteOptions,
  UseDeleteReturn,
  UseDeleteState,
  DeletePhase,
  DeleteHooks,
} 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?">
    Authorization runs securely on your server in `delete.guard`:

    ```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>
