### 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 (
);
}
```
--------------------------------
### 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 (
);
}
```
--------------------------------
### 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',
});
```