### Install Dependencies for Manual Setup Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/components/upload-dropzone.mdx Install necessary npm packages, including lucide-react and react-dropzone, for manual setup. Ensure shadcn/ui is already configured. ```npm npm i lucide-react react-dropzone ``` -------------------------------- ### Installation Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/components/upload-button.mdx Instructions for installing the Upload Button component using either the CLI or manually. ```APIDOC ## Installation ### CLI Installation ```bash npx shadcn@latest add @better-upload/upload-button ``` ### Manual Installation 1. **Install Dependencies**: ```npm npm i lucide-react ``` Also add the [shadcn/ui button](https://ui.shadcn.com/docs/components/button) component to your project, as the upload button is built on top of it. 2. **Copy Component Code**: Copy the provided `upload-button.tsx` code into your project's components directory. 3. **Update Import Paths**: Adjust the import paths in the component code to match your project's structure. ``` -------------------------------- ### Install @better-upload/client Source: https://github.com/nic13gamer/better-upload/blob/main/packages/client/CHANGELOG.md Instructions for removing the old 'better-upload' package and installing the new '@better-upload/client' package. ```bash npm remove better-upload npm install @better-upload/client ``` -------------------------------- ### Install Better Upload Packages Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/quickstart-single.mdx Install the necessary server and client packages for Better Upload using npm. ```bash npm i @better-upload/server @better-upload/client ``` -------------------------------- ### Install shadcn/ui Components Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/guides/forms/react-hook-form.mdx Install necessary shadcn/ui components for the form. Ensure React Hook Form and Zod are also installed. ```bash npx shadcn@latest add field input button ``` -------------------------------- ### Install Dependencies for Manual Setup Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/components/upload-button.mdx Manually install lucide-react and ensure the shadcn/ui button component is added to your project, as the upload button depends on it. ```npm npm i lucide-react ``` -------------------------------- ### Setup Next.js Upload Route Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/guides/forms/react-hook-form.mdx Configure the server-side upload route using Next.js and Better Upload. This example uses AWS S3 as the client and defines upload parameters like max files and size. ```typescript import { route, type Router } from '@better-upload/server'; import { toRouteHandler } from '@better-upload/server/adapters/next'; import { aws } from '@better-upload/server/clients'; const router: Router = { client: aws(), bucketName: 'my-bucket', routes: { form: route({ multipleFiles: true, maxFiles: 5, maxFileSize: 1024 * 1024 * 5, // 5MB onBeforeUpload() { return { generateObjectInfo: ({ file }) => ({ key: `form/${file.name}` }), }; }, }), }, }; export const { POST } = toRouteHandler(router); ``` -------------------------------- ### Install Upload Progress via CLI Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/components/upload-progress.mdx Use the shadcn-ui CLI to add the UploadProgress component to your project. ```bash npx shadcn@latest add @better-upload/upload-progress ``` -------------------------------- ### Run Development Server Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/README.md Commands to start the development server for the Next.js application using npm, pnpm, or yarn. ```bash npm run dev # or pnpm dev # or yarn dev ``` -------------------------------- ### Run Development Server with bun Source: https://github.com/nic13gamer/better-upload/blob/main/apps/example/README.md Use this command to start the Next.js development server with bun. Open http://localhost:3000 in your browser to view the application. ```bash bun dev ``` -------------------------------- ### Run Development Server with npm Source: https://github.com/nic13gamer/better-upload/blob/main/apps/example/README.md Use this command to start the Next.js development server with npm. Open http://localhost:3000 in your browser to view the application. ```bash npm run dev ``` -------------------------------- ### Install Upload Dropzone via CLI Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/components/upload-dropzone.mdx Use this command to add the UploadDropzone component to your project using the CLI. ```bash npx shadcn@latest add @better-upload/upload-dropzone ``` -------------------------------- ### Install New Server Package Source: https://github.com/nic13gamer/better-upload/blob/main/packages/server/CHANGELOG.md Update your project by removing the old 'better-upload' package and installing the new '@better-upload/server' package. This also involves removing the AWS SDK if it was previously used. ```bash npm remove better-upload @aws-sdk/client-s3 npm install @better-upload/server ``` -------------------------------- ### Install Upload Button via CLI Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/components/upload-button.mdx Use the shadcn-ui CLI to add the upload button component to your project. ```bash npx shadcn@latest add @better-upload/upload-button ``` -------------------------------- ### Run Development Server with pnpm Source: https://github.com/nic13gamer/better-upload/blob/main/apps/example/README.md Use this command to start the Next.js development server with pnpm. Open http://localhost:3000 in your browser to view the application. ```bash pnpm dev ``` -------------------------------- ### Run Development Server with yarn Source: https://github.com/nic13gamer/better-upload/blob/main/apps/example/README.md Use this command to start the Next.js development server with yarn. Open http://localhost:3000 in your browser to view the application. ```bash yarn dev ``` -------------------------------- ### Install Paste Upload Area via CLI Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/components/paste-upload-area.mdx Use the shadcn-ui CLI to add the Paste Upload Area component to your project. ```bash npx shadcn@latest add @better-upload/paste-upload-area ``` -------------------------------- ### S3 Bucket CORS Configuration Example Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/quickstart.mdx Example JSON configuration for CORS settings on an S3 bucket. Ensure your bucket allows requests from your application's origins. ```json [ { "AllowedOrigins": [ "http://localhost:3000", "https://example.com" // Add your domain here ], "AllowedMethods": ["GET", "PUT", "POST", "DELETE"], "AllowedHeaders": ["*"], "ExposeHeaders": ["ETag"] } ] ``` -------------------------------- ### Presign Get Object Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/helpers-server.mdx Generate a pre-signed URL to download an object. Commonly used for client-side downloads. ```APIDOC ## Presign Get Object ### Description Generate a pre-signed URL to download an object. Commonly used for client-side downloads. ### Method POST (or relevant method based on underlying SDK) ### Endpoint `/helpers/presignGetObject` (hypothetical endpoint for illustration) ### Parameters #### Request Body - **s3** (object) - Required - The S3 client instance. - **bucket** (string) - Required - The name of the S3 bucket. - **key** (string) - Required - The key of the object to download. - **expiresIn** (number) - Optional - The expiration time for the URL in seconds. ### Request Example ```json { "bucket": "my-bucket", "key": "example.png", "expiresIn": 3600 } ``` ### Response #### Success Response (200) - **url** (string) - The pre-signed URL for downloading the object. #### Response Example ```json { "url": "https://example-bucket.s3.amazonaws.com/example.png?AWSAccessKeyId=...&Expires=...&Signature=..." } ``` ``` -------------------------------- ### Configure Hono Server for Uploads Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/quickstart/multiple.mdx Set up a Hono application to handle file uploads. This example uses the `aws` client and specifies image file types with a maximum of 4 files. ```typescript // when using a separate backend server, make sure to update the `api` option on the client hooks. import { Hono } from 'hono'; import { handleRequest, route, type Router } from '@better-upload/server'; import { aws } from '@better-upload/server/clients'; const router: Router = { client: aws(), // or cloudflare(), backblaze(), tigris(), ... // [!code highlight] bucketName: 'my-bucket', // [!code highlight] routes: { images: route({ fileTypes: ['image/*'], multipleFiles: true, maxFiles: 4, }), }, }; const app = new Hono(); app.post('/upload', (c) => { return handleRequest(c.req.raw, router); }); export default app; ``` -------------------------------- ### Previous FileUploadInfo Usage Source: https://github.com/nic13gamer/better-upload/blob/main/packages/client/CHANGELOG.md Example showing the older way of accessing file upload information using 'objectKey'. ```tsx useUploadFile({ route: 'demo', onUploadComplete: ({ file }) => { console.log(file.objectKey); // object metadata and cacheControl are missing }, }); ``` -------------------------------- ### Configure Express Server for Uploads Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/quickstart/multiple.mdx Set up an Express.js server to handle file uploads using the `@better-upload/server/adapters/node` adapter. This example configures the AWS client and specifies upload route constraints. ```typescript // when using a separate backend server, make sure to update the `api` option on the client hooks. import express from 'express'; import { route, type Router } from '@better-upload/server'; import { toNodeHandler } from '@better-upload/server/adapters/node'; import { aws } from '@better-upload/server/clients'; const router: Router = { client: aws(), // or cloudflare(), backblaze(), tigris(), ... // [!code highlight] bucketName: 'my-bucket', // [!code highlight] routes: { images: route({ fileTypes: ['image/*'], multipleFiles: true, maxFiles: 4, }), }, }; const app = express(); app.post('/upload', toNodeHandler(router)); // only mount express json middleware AFTER upload router app.use(express.json()); app.listen(3000); ``` -------------------------------- ### Upload Single File with TanStack Query Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/guides/tanstack-query.mdx This example shows how to upload a single file using `uploadFile` and TanStack Query's `useMutation`. The `onFileStateChange` callback provides updates on the file's upload progress. ```tsx 'use client'; import { uploadFile } from '@better-upload/client'; import { useMutation } from '@tanstack/react-query'; export function Uploader() { const { mutate: upload, isPending } = useMutation({ mutationFn: async (file: File) => { return uploadFile({ file, route: 'form', onFileStateChange: ({ file }) => { // you handle the progress of the file console.log(file); }, }); }, onSuccess: ({ file, metadata }) => { console.log({ file, metadata, }); }, onError: (error) => { console.error(error); }, }); return ( { if (e.target.files?.[0]) { upload(e.target.files[0]); } }} /> ); } ``` -------------------------------- ### Hono API Route for Uploads Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/quickstart/single.mdx Set up a file upload endpoint using Hono. This example shows how to integrate Better Upload's request handling within a Hono route. ```typescript // when using a separate backend server, make sure to update the `api` option on the client hooks. import { Hono } from 'hono'; import { handleRequest, route, type Router } from '@better-upload/server'; import { aws } from '@better-upload/server/clients'; const router: Router = { client: aws(), // or cloudflare(), backblaze(), tigris(), ... // [!code highlight] bucketName: 'my-bucket', // [!code highlight] routes: { profile: route({ fileTypes: ['image/*'], }), }, }; const app = new Hono(); app.post('/upload', (c) => { return handleRequest(c.req.raw, router); }); export default app; ``` -------------------------------- ### Setup React Hook Form with useUploadFiles Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/guides/forms/react-hook-form.mdx Initialize the form using React Hook Form's `useForm` and integrate file upload handling with the `useUploadFiles` hook from Better Upload. Sets up default values and handles upload completion and errors. ```tsx import { useUploadFiles } from '@better-upload/client'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import * as z from 'zod'; const formSchema = z.object({ folderName: z.string().min(1, 'Folder name is required.'), objectKeys: z.array(z.string()).min(1, 'Upload at least one file.'), }); export function FormUploader() { const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { folderName: '', objectKeys: [], }, }); const uploader = useUploadFiles({ route: 'form', onUploadComplete: ({ files }) => { form.setValue( 'objectKeys', files.map((file) => file.objectInfo.key) ); }, onError: (error) => { form.setError('objectKeys', { message: error.message || 'An error occurred.', }); }, }); function onSubmit(data: z.infer) { // call your API here console.log(data); } return (
{/* ... */} {/* Build the form here */} {/* ... */}
); } ``` -------------------------------- ### Handling Upload Start with onUploadBegin Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/hooks-multiple.mdx The `onUploadBegin` event is triggered just before files are uploaded to S3, after pre-signed URLs have been obtained from the server. This is useful for logging or initiating UI changes. ```tsx useUploadFiles({ route: 'images', onUploadBegin: ({ files, metadata }) => { console.log('Upload begin'); }, }); ``` -------------------------------- ### Configure TanStack Start Server for Uploads Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/quickstart/multiple.mdx Integrate file upload handling within a TanStack Router application. The `handleRequest` function processes uploads using the configured router. ```typescript import { createFileRoute } from '@tanstack/react-router'; import { handleRequest, route, type Router } from '@better-upload/server'; import { aws } from '@better-upload/server/clients'; const router: Router = { client: aws(), // or cloudflare(), backblaze(), tigris(), ... // [!code highlight] bucketName: 'my-bucket', // [!code highlight] routes: { images: route({ fileTypes: ['image/*'], multipleFiles: true, maxFiles: 4, }), }, }; export const Route = createFileRoute('/api/upload')({ server: { handlers: { POST: async ({ request }) => { return handleRequest(request, router); }, }, }, }); ``` -------------------------------- ### Create Uploader Component with useUploadFile Hook Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/quickstart-single.mdx Build the UI component using the `useUploadFile` hook and `UploadButton`. Ensure the `route` matches your server configuration. This example is for Next.js and requires 'use client'. ```tsx 'use client'; // only for Next.js import { useUploadFile } from '@better-upload/client'; import { UploadButton } from '@/components/ui/upload-button'; export function Uploader() { const { control } = useUploadFile({ route: 'profile', }); return ; } ``` -------------------------------- ### Configure Fastify Server for Uploads Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/quickstart/multiple.mdx Integrate file upload handling into a Fastify application. This setup uses the AWS client and defines route-specific upload configurations. ```typescript // when using a separate backend server, make sure to update the `api` option on the client hooks. import Fastify from 'fastify'; import { route, type Router } from '@better-upload/server'; import { toNodeHandler } from '@better-upload/server/adapters/node'; import { aws } from '@better-upload/server/clients'; const router: Router = { client: aws(), // or cloudflare(), backblaze(), tigris(), ... // [!code highlight] bucketName: 'my-bucket', // [!code highlight] routes: { images: route({ fileTypes: ['image/*'], multipleFiles: true, maxFiles: 4, }), }, }; const app = Fastify(); app.post('/upload', toNodeHandler(router)); app.listen({ port: 3000 }); ``` -------------------------------- ### Single File Upload with useUploadFile Hook Source: https://context7.com/nic13gamer/better-upload/llms.txt Demonstrates how to use the useUploadFile hook for uploading a single file. Includes setup for progress tracking, lifecycle callbacks, and handling upload results. Use this for standard single file uploads. ```typescript 'use client'; import { useUploadFile } from '@better-upload/client'; import { useState } from 'react'; export function AvatarUploader() { const [previewUrl, setPreviewUrl] = useState(null); const { upload, uploadAsync, reset, progress, isPending, isSuccess, isError, error, uploadedFile, control, } = useUploadFile({ route: 'avatar', api: '/api/upload', // default // Optional: modify file before upload onBeforeUpload: async ({ file }) => { if (file.size > 1024 * 1024) { return await compressImage(file); } return file; }, onUploadBegin: ({ file, metadata }) => { console.log('Starting upload:', file.name); setPreviewUrl(URL.createObjectURL(file.raw)); }, onUploadProgress: ({ file }) => { console.log(`Progress: ${Math.round(file.progress * 100)}%`); }, onUploadComplete: async ({ file, metadata }) => { console.log('Upload complete:', file.objectInfo.key); await notifyServer(metadata.uploadId); }, onUploadSettle: ({ file }) => { // Called regardless of success/failure console.log('Upload settled'); }, onError: (error) => { console.error('Upload failed:', error.type, error.message); }, }); const handleFileSelect = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; // Option 1: Non-throwing upload const result = await upload(file, { metadata: { userId: '123' }, // sent to server }); if (result.file) { console.log('Uploaded to:', result.file.objectInfo.key); } // Option 2: Throwing upload (use in try/catch) try { const { file: uploaded, metadata } = await uploadAsync(file); console.log('Success:', uploaded.objectInfo.key); } catch (err) { console.error('Failed:', err); } }; return (
{isPending && (
{Math.round(progress * 100)}%
)} {isSuccess && uploadedFile && (

Uploaded: {uploadedFile.objectInfo.key}

)} {isError &&

Error: {error?.message}

}
); } ``` -------------------------------- ### Setup TanStack Form with File Uploads Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/guides/forms/tanstack-form.mdx Initialize a TanStack Form instance with Zod validation and integrate the `useUploadFiles` hook from `@better-upload/client`. The `onUploadComplete` callback updates the form's 'objectKeys' field with the keys of the uploaded files. ```typescript import { useUploadFiles } from '@better-upload/client'; import { useForm } from '@tanstack/react-form'; import * as z from 'zod'; const formSchema = z.object({ folderName: z.string().min(1, 'Folder name is required.'), objectKeys: z.array(z.string()).min(1, 'Upload at least one file.'), }); export function FormUploader() { const form = useForm({ defaultValues: { folderName: '', objectKeys: [] as string[], }, validators: { onSubmit: formSchema, }, onSubmit: ({ value }) => { // call your API here console.log(value); }, }); const uploader = useUploadFiles({ route: 'form', onUploadComplete: ({ files }) => { form.setFieldValue( 'objectKeys', files.map((file) => file.objectInfo.key) ); }, }); return (
{ e.preventDefault(); form.handleSubmit(); }} > {/* ... */}
); } ``` -------------------------------- ### Get Object Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/helpers-server.mdx Get an object (including its content) from an S3 bucket. ```APIDOC ## Get Object ### Description Get an object (including its content) from an S3 bucket. ### Method POST (or relevant method based on underlying SDK) ### Endpoint `/helpers/getObject` (hypothetical endpoint for illustration) ### Parameters #### Request Body - **s3** (object) - Required - The S3 client instance. - **bucket** (string) - Required - The name of the S3 bucket. - **key** (string) - Required - The key of the object to retrieve. ### Request Example ```json { "bucket": "my-bucket", "key": "example.png" } ``` ### Response #### Success Response (200) - **objectBlob** (Blob) - The object content as a Blob. - **objectStream** (ReadableStream) - The object content as a ReadableStream. #### Response Example (Blob) ```json { "objectBlob": "" } ``` #### Response Example (Stream) ```json { "objectStream": "" } ``` ``` -------------------------------- ### Set up Server for Uploads Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/quickstart-single.mdx Configure the server to generate pre-signed URLs for file uploads. Replace 'my-bucket' with your actual bucket name and select an S3 client. ```typescript import { createUploadRoute } from '@better-upload/server'; export const POST = createUploadRoute({ route: 'profile', bucket: 'my-bucket', // ... your S3 client configuration }); ``` -------------------------------- ### Set up Server for Uploads Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/quickstart.mdx Configure your server to generate pre-signed URLs for direct file uploads to an S3-compatible bucket. Remember to replace 'my-bucket' with your actual bucket name. ```typescript import { createUploadRoute } from '@better-upload/server'; export const { POST } = createUploadRoute({ // Change my-bucket to your bucket name // Choose your S3 client: https://better-upload.com/docs/helpers-server#s3-clients client: 'my-bucket', // Upload routes are configured here // Learn more: https://better-upload.com/docs/routes-multiple#upload-routes route: 'images', }); ``` -------------------------------- ### Head Object Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/helpers-server.mdx Get metadata about an object without fetching its content. ```APIDOC ## Head Object ### Description Get metadata about an object without fetching its content. ### Method POST (or relevant method based on underlying SDK) ### Endpoint `/helpers/headObject` (hypothetical endpoint for illustration) ### Parameters #### Request Body - **s3** (object) - Required - The S3 client instance. - **bucket** (string) - Required - The name of the S3 bucket. - **key** (string) - Required - The key of the object to get metadata for. ### Request Example ```json { "bucket": "my-bucket", "key": "example.png" } ``` ### Response #### Success Response (200) - **objectMetadata** (object) - An object containing the metadata of the S3 object. #### Response Example ```json { "objectMetadata": { "ContentLength": 1024, "ContentType": "image/png", "LastModified": "2023-10-27T10:00:00Z" } } ``` ``` -------------------------------- ### Update FileUploadInfo Usage Source: https://github.com/nic13gamer/better-upload/blob/main/packages/client/CHANGELOG.md Example demonstrating the change from accessing 'objectKey' to 'objectInfo.key' in the FileUploadInfo object. ```tsx useUploadFile({ route: 'demo', onUploadComplete: ({ file }) => { console.log(file.objectInfo.key); // there is also .metadata and .cacheControl }, }); ``` -------------------------------- ### Configure MinIO Client Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/helpers-server.mdx Set up the MinIO client, including region, endpoint, and access credentials. ```typescript import { minio } from '@better-upload/server/clients'; const s3 = minio({ region: 'your-minio-region', endpoint: 'https://minio.example.com', accessKeyId: 'your-access-key-id', secretAccessKey: 'your-secret-access-key', }); ``` -------------------------------- ### Configure Wasabi Client Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/helpers-server.mdx Initialize the Wasabi client by providing your region and access keys. ```typescript import { wasabi } from '@better-upload/server/clients'; const s3 = wasabi({ region: 'your-wasabi-region', accessKeyId: 'your-access-key-id', secretAccessKey: 'your-secret-access-key', }); ``` -------------------------------- ### CORS Configuration for S3 Bucket Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/quickstart-single.mdx Example CORS configuration for an S3 bucket. Ensure 'AllowedOrigins' includes your application's domain. ```json [ { "AllowedOrigins": [ "http://localhost:3000", "https://example.com" // Add your domain here ], "AllowedMethods": ["GET", "PUT", "POST", "DELETE"], "AllowedHeaders": ["*"], "ExposeHeaders": ["ETag"] } ] ``` -------------------------------- ### Get Object Tagging Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/helpers-server.mdx Retrieve tags associated with an S3 object. The function returns both a raw tags string and a parsed object representation. ```typescript import { getObjectTagging } from '@better-upload/server/helpers'; const { tags, tagsObject } = await getObjectTagging(s3, { bucket: 'my-bucket', key: 'example.png', }); ``` -------------------------------- ### Upload Button Component Code Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/components/upload-button.mdx This is the core component code for the upload button. Ensure import paths are updated to match your project setup. ```tsx import React from "react"; import { Slot } from "@radix-ui/react-slot"; import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@/lib/utils"; const uploadButtonVariants = cva( "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background hover:bg-accent hover:text-accent-foreground h-10 py-2 px-4", { variants: { variant: { default: "bg-primary text-primary-foreground hover:bg-primary/90", destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", outline: "border border-input hover:bg-accent hover:text-accent-foreground", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground", }, size: { default: "h-10 py-2 px-4", sm: "h-9 px-3 rounded-md", lg: "h-11 px-8 rounded-md", }, }, defaultVariants: { variant: "default", size: "default", }, } ); export interface UploadButtonProps extends React.ButtonHTMLAttributes, VariantProps { asChild?: boolean; control: any; accept?: string; metadata?: Record; uploadOverride?: (file: File) => Promise; } const UploadButton = React.forwardRef( ( { className, variant, size, asChild = false, control, accept, metadata, uploadOverride, ...props }, ref ) => { const Comp = asChild ? Slot : "button"; const onClick = async (e: React.MouseEvent) => { e.preventDefault(); const input = document.createElement("input"); input.type = "file"; if (accept) input.accept = accept; input.click(); input.onchange = async () => { if (!input.files) return; const file = input.files[0]; if (!file) return; if (uploadOverride) { await uploadOverride(file); return; } const formData = new FormData(); formData.append("file", file); if (metadata) { Object.entries(metadata).forEach(([key, value]) => { formData.append(key, JSON.stringify(value)); }); } await fetch(`/api/upload?route=${control.route}`, { method: "POST", body: formData, }); }; }; return ( ); } ); UploadButton.displayName = "UploadButton"; export { UploadButton, uploadButtonVariants }; ``` -------------------------------- ### Place Uploader Component in Page Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/quickstart.mdx Integrate the created `` component into your React page. This example shows how to render it within a main layout. ```tsx import { Uploader } from '@/components/uploader'; export default function Page() { return (
); } ``` -------------------------------- ### Fastify API Route for Uploads Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/quickstart/single.mdx Integrate Better Upload with Fastify for handling file uploads. This example utilizes the `toNodeHandler` adapter for Fastify. ```typescript // when using a separate backend server, make sure to update the `api` option on the client hooks. import Fastify from 'fastify'; import { route, type Router } from '@better-upload/server'; import { toNodeHandler } from '@better-upload/server/adapters/node'; import { aws } from '@better-upload/server/clients'; const router: Router = { client: aws(), // or cloudflare(), backblaze(), tigris(), ... // [!code highlight] bucketName: 'my-bucket', // [!code highlight] routes: { profile: route({ fileTypes: ['image/*'], }), }, }; const app = Fastify(); app.post('/upload', toNodeHandler(router)); app.listen({ port: 3000 }); ``` -------------------------------- ### Configure Backblaze B2 Client Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/helpers-server.mdx Initialize the Backblaze B2 client using your region, application key ID, and application key. ```typescript import { backblaze } from '@better-upload/server/clients'; const s3 = backblaze({ region: 'your-backblaze-region', applicationKeyId: 'your-application-key-id', applicationKey: 'your-application-key', }); ``` -------------------------------- ### Express API Route for Uploads Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/quickstart/single.mdx Configure an Express.js application to handle file uploads using Better Upload. This example uses the `toNodeHandler` adapter. ```typescript // when using a separate backend server, make sure to update the `api` option on the client hooks. import express from 'express'; import { route, type Router } from '@better-upload/server'; import { toNodeHandler } from '@better-upload/server/adapters/node'; import { aws } from '@better-upload/server/clients'; const router: Router = { client: aws(), // or cloudflare(), backblaze(), tigris(), ... // [!code highlight] bucketName: 'my-bucket', // [!code highlight] routes: { profile: route({ fileTypes: ['image/*'], }), }, }; const app = express(); app.post('/upload', toNodeHandler(router)); // only mount express json middleware AFTER upload router app.use(express.json()); app.listen(3000); ``` -------------------------------- ### Configure Linode Client Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/helpers-server.mdx Initialize the Linode Object Storage client with your region, access key, and secret key. ```typescript import { linode } from '@better-upload/server/clients'; const s3 = linode({ region: 'your-linode-region', accessKey: 'your-access-key', secretKey: 'your-secret-key', }); ``` -------------------------------- ### Configure DigitalOcean Spaces Client Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/helpers-server.mdx Set up the DigitalOcean Spaces client with your region, key, and secret. ```typescript import { digitalOcean } from '@better-upload/server/clients'; const s3 = digitalOcean({ region: 'your-spaces-region', key: 'your-spaces-key', secret: 'your-spaces-secret', }); ``` -------------------------------- ### Basic File Upload with useUploadFiles Hook Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/hooks-multiple.mdx Demonstrates how to use the `useUploadFiles` hook to handle file uploads. Attach the `upload` function to an input's `onChange` event. Ensure the `route` option matches your server configuration. ```tsx import { useUploadFiles } from '@better-upload/client'; export function Uploader() { const { upload, uploadAsync, reset, uploadedFiles, failedFiles, progresses, isPending, isAborted, isSettled, isError, allSucceeded, hasFailedFiles, averageProgress, error, metadata, control, // for use in pre-built components } = useUploadFiles({ route: 'images', }); return ( { if (e.target.files) { upload(e.target.files); } }} /> ); } ``` -------------------------------- ### Configure AWS S3 Client Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/helpers-server.mdx Use this snippet to initialize the AWS S3 client. Ensure you provide your AWS credentials and region. ```typescript import { aws } from '@better-upload/server/clients'; const s3 = aws({ accessKeyId: 'your-access-key-id', secretAccessKey: 'your-secret-access-key', region: 'us-east-1', }); ``` -------------------------------- ### Configure Remix Server for Uploads Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/quickstart/multiple.mdx Implement file upload functionality in a Remix application using the `handleRequest` function. This example uses AWS S3 as the client. ```typescript import { ActionFunctionArgs } from '@remix-run/node'; import { handleRequest, route, type Router } from '@better-upload/server'; import { aws } from '@better-upload/server/clients'; const router: Router = { client: aws(), // or cloudflare(), backblaze(), tigris(), ... // [!code highlight] bucketName: 'my-bucket', // [!code highlight] routes: { images: route({ fileTypes: ['image/*'], multipleFiles: true, maxFiles: 4, }), }, }; export async function action({ request }: ActionFunctionArgs) { return handleRequest(request, router); } ``` -------------------------------- ### Configure S3 Client for Various Providers Source: https://context7.com/nic13gamer/better-upload/llms.txt Sets up clients for different S3-compatible storage services using environment variables or explicit parameters. Supports AWS S3, Cloudflare R2, DigitalOcean Spaces, and custom services. ```typescript import { aws, cloudflare, digitalocean, backblaze, minio, wasabi, custom } from '@better-upload/server/clients'; // AWS S3 - uses AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION env vars const awsClient = aws(); // Or with explicit credentials const awsClientExplicit = aws({ accessKeyId: 'AKIAIOSFODNN7EXAMPLE', secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', region: 'us-east-1', sessionToken: 'optional-session-token', }); // Cloudflare R2 const r2Client = cloudflare({ accountId: 'your-account-id', accessKeyId: 'your-access-key', secretAccessKey: 'your-secret-key', jurisdiction: 'eu', // optional: 'eu' or 'fedramp' }); // DigitalOcean Spaces const doClient = digitalocean({ accessKeyId: 'your-access-key', secretAccessKey: 'your-secret-key', region: 'nyc3', }); // Custom S3-compatible service const customClient = custom({ host: 'minio.example.com', accessKeyId: 'your-access-key', secretAccessKey: 'your-secret-key', region: 'us-east-1', secure: true, forcePathStyle: true, // for MinIO/self-hosted }); // Use in router const router: Router = { client: r2Client, bucketName: 'my-bucket', routes: { /* ... */ }, }; ``` -------------------------------- ### Configure Cloudflare R2 Client Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/components/mdx/helpers-server.mdx Initialize the Cloudflare R2 client by providing your account ID and access keys. ```typescript import { cloudflare } from '@better-upload/server/clients'; const s3 = cloudflare({ accountId: 'your-account-id', accessKeyId: 'your-access-key-id', secretAccessKey: 'your-secret-access-key', }); ``` -------------------------------- ### Create Uploader Component with UploadDropzone Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/quickstart.mdx Build a React UI component using `UploadDropzone` for multiple file uploads and the `useUploadFiles` hook. Ensure you have installed the shadcn UI component. ```tsx 'use client'; // only for Next.js import { useUploadFiles } from '@better-upload/client'; import { UploadDropzone } from '@/components/ui/upload-dropzone'; export function Uploader() { const { control } = useUploadFiles({ route: 'images', }); return ( ); } ``` -------------------------------- ### Get Object as Blob or Stream Source: https://github.com/nic13gamer/better-upload/blob/main/apps/docs/content/docs/helpers-server.mdx Retrieve an object's content from an S3 bucket. Can be fetched as a Blob or a ReadableStream. Specify the S3 client, bucket name, and object key. ```typescript import { getObjectBlob, getObjectStream } from '@better-upload/server/helpers'; // as a Blob const objectBlob = await getObjectBlob(s3, { bucket: 'my-bucket', key: 'example.png', }); // as a ReadableStream const objectStream = await getObjectStream(s3, { bucket: 'my-bucket', key: 'example.png', }); ```