# Helpers (https://v1.s3.dimah.dev/docs/react/helpers)

`@dimah-s3/react` and `@dimah-s3/core` export utilities for formatting file
sizes, progress labels, hashing, and error localization.

## Progress and speed formatting [#progress-and-speed-formatting]

```ts
import { formatUploadProgress, formatSpeed, formatEta } from "@dimah-s3/react";

formatUploadProgress(1_200_000, 2_000_000, 60); // "1.1 MB / 1.9 MB (60%)"
formatSpeed(1_200_000); // "1.1 MB/s"
formatEta(800_000, 200_000); // "4s"
```

---

## File size and validation [#file-size-and-validation]

```ts
import { formatFileSize, truncateFileName, validateFile } from "@dimah-s3/core";

formatFileSize(1_500_000); // "1.4 MB"
formatFileSize(1_500_000, { locale: "fa", units: { MB: "مگابایت" } }); // "۱٫۴ مگابایت"
truncateFileName("my-avatar-profile-picture-long-name.png", 20); // "my-avatar-profi… .png"

const error = validateFile(file, {
  accept: ["image/*"],
  maxFileSize: 2 * 1024 * 1024,
});
// returns null if valid, or { code: "FILE_TYPE_NOT_ALLOWED", message }
```

In React, `useFormatFileSize()` and `useFormatNumber()` follow the `locale`
on `S3Provider` and take unit labels from your translations. The built-in UI
uses them for sizes, counts, and progress. See
[Internationalization](https://v1.s3.dimah.dev/docs/i18n#numbers-and-sizes).

---

## Checksums and public URLs [#checksums-and-public-urls]

```ts
import { buildPublicObjectUrl, sha256File } from "@dimah-s3/core";

// SHA-256 base64 digest
const hash = await sha256File(file);

// Public CDN URL helper
const url = buildPublicObjectUrl({
  baseUrl: "https://cdn.example.com",
  key: "avatar/uuid/avatar.png",
});
```

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

<Accordions>

<Accordion title="How do I format API errors in toast notifications?">

Format the error with `useFormatDimahError` before passing it to your toast
library:

```tsx
import { useFormatDimahError, useUpload } from "@dimah-s3/react";

export function AvatarUploader() {
  const formatError = useFormatDimahError();

  const upload = useUpload({
    route: "avatar",
    onError: (err) => {
      toast.error(formatError(err));
    },
  });
}
```

</Accordion>

</Accordions>
