@s3-good/react
v0.2.0
Published
React UI primitives and typed helpers for `s3-good` upload routes.
Readme
@s3-good/react
React UI primitives and typed helpers for s3-good upload routes.
Installation
pnpm add @s3-good/react s3-good zodPeer dependencies:
react >= 18react-dom >= 18
Import styles once in your app entry:
import "@s3-good/react/styles.css";Recommended API: generateReactHelpers
import { generateReactHelpers } from "@s3-good/react";
import type { OurFileRouter } from "~/server/upload-router";
export const {
useUpload,
uploadFiles,
createUpload,
enqueueUpload,
getQueueState,
resumePending,
} =
generateReactHelpers<OurFileRouter>({
url: "/api/upload", // default
});This gives:
- endpoint autocomplete from your
FileRouter - typed
inputvalues per endpoint - typed
serverDatain responses - queue helpers for imperative flows
Components
UploadButton
import { UploadButton } from "@s3-good/react";
<UploadButton
endpoint="imageUploader"
onClientUploadComplete={(files) => {
console.log(files);
}}
onUploadError={(error) => {
console.error(error.message);
}}
/>;UploadDropzone
import { UploadDropzone } from "@s3-good/react";
<UploadDropzone
endpoint="imageUploader"
onPaste
onClientUploadComplete={(files) => {
console.log(files);
}}
/>;Component notes
endpointis required.modesupports"auto"and"manual".headerscan be static or function-based.appearanceandcontentlet you customize visuals/text without forking components.
Hook API
useUpload
import { useUpload } from "~/lib/upload"; // from generateReactHelpers
function CustomUploader() {
const { startUpload, isUploading, progress, abort, permittedFileInfo } =
useUpload("imageUploader", {
onUploadError: (error) => console.error(error.message),
});
return (
<div>
<input
type="file"
onChange={(event) => {
const files = Array.from(event.target.files ?? []);
if (files.length) {
void startUpload(files);
}
}}
/>
{isUploading ? <progress max={100} value={progress} /> : null}
<button type="button" onClick={abort}>Cancel</button>
{permittedFileInfo ? <p>Allowed: {permittedFileInfo.fileTypes.join(", ")}</p> : null}
</div>
);
}Returns:
startUpload(files, input?)isUploadingprogress(0-100)abort()enqueue(files, input?)pause(jobId),resume(jobId),cancel(jobId),retry(jobId)jobs,activeCount,queueSize,failedCountpermittedFileInfo
Queue and retry options are configurable via hook options:
queue?: { concurrency?: number; autoStart?: boolean }retry?: { maxAttempts?: number; baseDelayMs?: number; maxDelayMs?: number; jitter?: boolean }resume?: { enabled?: boolean; storageKey?: string }(enableddefaults tofalse)
retry(jobId) enqueues a new job using the last known args for that failed job.
Lower-level exports
Also exported for custom UI compositions:
FileListFilePreviewProgressBar- style helpers (
resolveStyle,resolveClassName,cn, variants)
Next.js notes
- This package is client-side UI; use it from Client Components.
- Your server handlers still live in
s3-good/nextroute files.
If using a monorepo + Next.js, keep transpilePackages configured in next.config.ts when needed:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
transpilePackages: ["s3-good", "@s3-good/react"],
};
export default nextConfig;License
MIT
