Setup
Minimal client setup with createS3Client and one upload button.
Install
npm i @dimah-s3/reactAPI client
Hooks use an S3Api client. Prefer createS3Client — one call gives you the
client, a bound provider, and a typed useApi.
If your backend uses @dimah-s3/server on /api/s3:
"use client";
import { createS3Client } from "@dimah-s3/react";
export const { api, S3Provider, useApi } = createS3Client();
export function S3ClientProvider({ children }: { children: React.ReactNode }) {
return <S3Provider>{children}</S3Provider>;
}Custom base path / plugins / auth headers:
import { createS3Client } from "@dimah-s3/react";
import { dbClient } from "@dimah-s3/db/client";
export const { api, S3Provider, useApi } = createS3Client({
basePath: "/api/your-path",
plugins: [dbClient()],
// cookies (same-origin) or Authorization via headers factory
credentials: "include",
headers: async () => ({
Authorization: `Bearer ${await getAccessToken()}`,
}),
});Failed API calls throw DimahS3Error from @dimah-s3/core (with .status). Upload engine failures throw S3UploadError (extends it — .code, .status, .phase).
Use api directly (like Better Auth's client) or useApi() inside React.
Provider
Mount the provider once near your app root.
import { S3ClientProvider } from "@/components/s3-provider";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<S3ClientProvider>{children}</S3ClientProvider>
</body>
</html>
);
}Using @dimah-s3/ui? Also mount <Toaster /> from @dimah-s3/ui (or @/components/ui/toast for registry) next to the provider — see UI setup.
Now hooks inside this provider can omit api.
First hook
"use client";
import { useUploadControls } from "@dimah-s3/react";
export function Uploader() {
const { openFilePicker, inputProps, progress, isUploading } =
useUploadControls({
objectKey: (file) => `uploads/${Date.now()}-${file.name}`,
accept: ["image/*", ".pdf"],
maxFileSize: 10 * 1024 * 1024,
onSuccess: (_file, result) => console.log(result.key),
});
return (
<div>
<input {...inputProps} />
<button type="button" onClick={openFilePicker} disabled={isUploading}>
{isUploading ? `Uploading ${progress.percent}%` : "Upload file"}
</button>
</div>
);
}Upload-only server config (recommended start)
If your first screen only needs upload, keep other operations off:
import { dimahS3 } from "@dimah-s3/server";
import { s3Client, defaultBucket } from "@/lib/s3-client";
export const s3 = dimahS3({
s3: s3Client,
defaultBucket,
upload: { enabled: true },
download: { enabled: false },
delete: { enabled: false },
multipart: { enabled: false },
});Enable other features later when you need them.
Resumable uploads (upload store)
For multipart resume across refreshes, add an upload store:
import { createLocalStorageStore } from "@dimah-s3/react";
export const localStorageStore = createLocalStorageStore();import { useUploadControls } from "@dimah-s3/react";
import { localStorageStore } from "@/lib/upload-store";
const { openFilePicker, inputProps } = useUploadControls({
objectKey: (file) => `uploads/${Date.now()}-${file.name}`,
multipart: true,
uploadStore: localStorageStore,
});Full guide: Upload store.