# Custom UI (https://v2.s3.dimah.dev/docs/react/ui/customization/custom-ui)

Combine `@dimah-s3/react` hooks with your own components or custom shadcn primitives.

## Preview [#preview]

<DemoPreview name="custom-upload-demo.tsx">
  <CustomUploadDemo />
</DemoPreview>

## Example: Headless File Uploader [#example-headless-file-uploader]

```tsx
"use client";

import { useUpload } from "@dimah-s3/react";
import { Button } from "@/components/ui/button";

export function CustomFilePicker() {
  const upload = useUpload({ route: "avatar" });

  return (
    <div className="flex items-center gap-4">
      <input {...upload.getInputProps()} />
      <Button type="button" onClick={() => upload.open()}>
        {upload.isPending
          ? `Uploading ${upload.progress.percent}%`
          : "Choose file"}
      </Button>
    </div>
  );
}
```

## Example: A part of your own [#example-a-part-of-your-own]

Inside [`Upload`](https://v2.s3.dimah.dev/docs/react/ui/components/upload), `useUploadContext()`
returns the root's hook. Build a part that sits next to `UploadTrigger` and
`UploadStatus`:

```tsx
"use client";

import { useUploadContext } from "@dimah-s3/ui";

export function UploadCount() {
  const { upload } = useUploadContext();
  const done = upload.files.filter((file) => file.phase === "success").length;

  if (upload.files.length === 0) return null;
  return (
    <p className="text-muted-foreground text-sm">
      {done} of {upload.files.length} uploaded
    </p>
  );
}
```

`useDownloadContext()` and `useDeleteContext()` do the same inside
`Download` and `Delete`.

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

<Accordions>

<Accordion title="How do I access low-level transfer metrics?">

Read `upload.progress` for the bytes sent across the queue, and
`file.progress` for one file:

```tsx
const { progress, files } = useUpload({ route: "avatar" });

console.log(progress.loaded, progress.total, progress.percent);
console.log(files[0]?.progress.percent);
```

</Accordion>

</Accordions>
