Try it

Pick a file once — Create session and PUT upload use the same name, type, and size.

Upload file
No file chosen

Required for upload session and CDN PUT examples.

Packages

@reupload/react

View on npm →

Ready-to-use React file uploaders. Import a component, set your backend URL, and upload — built on @reupload/client (CDN flow only).

All packages · @reupload/sdk · @reupload/client

Install

terminal
npm install @reupload/react @reupload/client

Setup

Import styles once in your app entry or root layout:

app/layout.tsx
import "@reupload/react/styles.css";
 
// .env.local
// NEXT_PUBLIC_API_URL=http://localhost:3001

Optional provider for shared config:

providers.tsx
import { ReuploadProvider } from "@reupload/react";
 
export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <ReuploadProvider apiUrl={process.env.NEXT_PUBLIC_API_URL}>
      {children}
    </ReuploadProvider>
  );
}

Your server must implement the backend file router using @reupload/sdk.

Uploaders

Import any uploader directly — no extra wiring beyond env vars.

FileUploader

Labeled button with progress and status.

UploadForm.tsx
import { FileUploader } from "@reupload/react";
 
export function UploadForm() {
  return (
    <FileUploader
      label="Choose document"
      accept="application/pdf,image/*"
      onUploadComplete={({ fileId }) => console.log("Done", fileId)}
    />
  );
}

ButtonUploader

Minimal upload button.

UploadButton.tsx
import { ButtonUploader } from "@reupload/react";
 
<ButtonUploader buttonText="Upload" accept="image/*" />;

DropzoneUploader

Drag-and-drop zone with click-to-browse.

Dropzone.tsx
import { DropzoneUploader } from "@reupload/react";
 
<DropzoneUploader
  title="Drop images here"
  description="PNG or JPG, up to 50 MB"
  accept="image/*"
  multiple
/>;

AvatarUploader

Circular profile photo picker. Defaults to image/* and 2 MB validation.

Avatar.tsx
import { AvatarUploader } from "@reupload/react";
 
<AvatarUploader
  src={user.avatarUrl}
  onUploadComplete={({ fileId }) => saveAvatar(fileId)}
/>;

MultiFileUploader

Queue multiple files with per-file status.

MultiUpload.tsx
import { MultiFileUploader } from "@reupload/react";
 
<MultiFileUploader
  maxFiles={10}
  accept="image/*"
  onFilesComplete={(results) =>
    console.log(results.map((r) => r.fileId))
  }
/>;

Confirm before upload

Set immediateUpload={false} to stage files and show a built-in Confirm / Cancel bar. Use onBeforeUpload for an optional async gate when the user confirms.

ConfirmUpload.tsx
import { FileUploader } from "@reupload/react";
 
<FileUploader
  label="Choose document"
  immediateUpload={false}
  onBeforeUpload={async (files) =>
    window.confirm(`Upload ${files[0]?.name ?? "file"}?`)
  }
  onUploadComplete={({ fileId }) => console.log("Done", fileId)}
/>;

Common props

PropDescription
clientCustom ReuploadClient
validation{ maxBytes, accept }
acceptMIME string or array
multipleMulti-select (where supported)
pollPoll until COMPLETED (default false)
hideStatusHide built-in progress/status UI (default true)
onProgressCDN progress callback (only tracked when set)
immediateUploadUpload on select (default true; set false to confirm first)
onBeforeUploadAsync gate on confirm — return false to abort
renderPendingConfirmCustom confirm UI when immediateUpload={false}
onUploadCompleteCallback with fileId
onUploadErrorError message callback
classNameRoot CSS class override
childrenRender prop for fully custom UI

Custom UI

CustomDropzone.tsx
import { DropzoneUploader, useUploader } from "@reupload/react";
 
// Render prop
<DropzoneUploader>
  {({ isUploading, openFilePicker, state, hasPendingUpload, confirmUpload, cancelPending }) => (
    <div>
      <div onClick={openFilePicker}>
        {isUploading ? "Uploading…" : "Click to upload"}
        {state.status === "completed" ? " ✓" : null}
      </div>
      {hasPendingUpload ? (
        <>
          <button type="button" onClick={() => void confirmUpload()}>Confirm</button>
          <button type="button" onClick={cancelPending}>Cancel</button>
        </>
      ) : null}
    </div>
  )}
</DropzoneUploader>
 
// Or use useUploader hook directly
const { uploadFile, state, openFilePicker, pendingFiles, confirmUpload } = useUploader({
  immediateUpload: false,
});

Styling

Override CSS variables on a parent element:

theme.css
.my-app {
  --reupload-primary: #7c3aed;
  --reupload-radius: 0.75rem;
}

Exports

ImportContents
@reupload/reactUploaders, ReuploadProvider, useUploader
@reupload/react/styles.cssDefault component styles