next-media-uploader
v0.2.3
Published
Next.js media uploads via Bucket Backend — API key only
Maintainers
Readme
next-media-uploader
Media uploads for Next.js apps using the Bucket Backend API. Add one API key — storage and infrastructure are handled by this package.
How it works
Browser → your Next.js route (/api/upload/bucket)
→ Bucket Backend (built into this package)
→ secure file storageInstall
npm install next-media-uploaderSetup (one env variable)
Add to .env.local:
BUCKET_API_KEY=your_api_key| Variable | Required | Description |
|---|---|---|
| BUCKET_API_KEY | Yes | API key from the Bucket dashboard (server-only — never use NEXT_PUBLIC_) |
That's it. No extra storage credentials or backend configuration needed.
NEXT_PUBLIC_UPLOAD_PROVIDER=bucket is optional — bucket is already the default.
Get your API key: Bucket Backend dashboard → API Key → Generate.
Restart your dev server after adding the key.
Quick start
1. API route
// app/api/upload/bucket/route.ts
import { createBucketUploadRoute } from "next-media-uploader/next";
export const POST = createBucketUploadRoute();2. React component
"use client";
import { Uploader } from "next-media-uploader/react";
export function ImageUpload({ onUploaded }: { onUploaded: (url: string) => void }) {
return (
<Uploader
provider="bucket"
accept="image"
maxSize={5 * 1024 * 1024}
onSuccess={(result) => onUploaded(result.url)}
/>
);
}3. Hook only
const { upload, uploadMultiple, isUploading, url } = useUpload({
provider: "bucket",
accept: "image",
maxSize: 5 * 1024 * 1024,
});
// Single file
await upload(file);
// Multiple files (one request, up to 20 files)
await uploadMultiple(files);Single vs multiple uploads
One Bucket Backend endpoint handles both patterns:
| Upload | Form field | Response |
|--------|------------|----------|
| Single | file | { key, url, contentType } |
| Multiple | files (repeated, max 20) | { count, results: [...] } |
Component with multiple files:
<Uploader
provider="bucket"
accept="image"
multiple
maxSize={5 * 1024 * 1024}
onBatchSuccess={(batch) => setImages(batch.results.map((r) => r.url))}
/>Server-side multiple upload:
import { uploadFilesToBucketApi } from "next-media-uploader/bucket-api";
const batch = await uploadFilesToBucketApi(files);
// { count: 2, results: [{ key, url, contentType }, ...] }Custom UI
Default
<Uploader provider="bucket" accept="image" maxSize={5 * 1024 * 1024} />Slots
<Uploader
provider="bucket"
accept="image"
slots={{
trigger: ({ openFilePicker, isUploading }) => (
<button onClick={openFilePicker} disabled={isUploading}>
Pick a photo
</button>
),
preview: ({ result, reset }) => (
<div>
<img src={result?.url} alt="" />
<button onClick={reset}>Remove</button>
</div>
),
}}
/>Full control
import { Uploader, UploaderInput } from "next-media-uploader/react";
<Uploader provider="bucket" accept="video" maxSize={100 * 1024 * 1024}>
{({ openFilePicker, isUploading, result, inputRef, getInputProps }) => (
<div>
<button onClick={openFilePicker} disabled={isUploading}>Upload</button>
<UploaderInput inputRef={inputRef} getInputProps={getInputProps} />
{result && <video src={result.url} controls />}
</div>
)}
</Uploader>API reference
| Export | From | Description |
|---|---|---|
| useUpload(options) | next-media-uploader/react | Upload hook |
| Uploader | next-media-uploader/react | Upload component with custom UI |
| createBucketUploadRoute() | next-media-uploader/next | API route factory (single + multiple) |
| uploadFileToBucketApi() | next-media-uploader/bucket-api | Server-side single upload |
| uploadFilesToBucketApi() | next-media-uploader/bucket-api | Server-side multiple upload |
Options
| Option | Type | Default | Description |
|---|---|---|---|
| provider | "bucket" | "bucket" | Storage provider |
| accept | "image" \| "video" \| "both" | "both" | Allowed file types |
| maxSize | number | — | Max bytes |
| uploadUrl | string | "/api/upload/bucket" | Proxy route |
| onProgress | (0–1) => void | — | Upload progress |
Upload result
{ key: string; url: string; contentType: string }Multiple upload:
{ count: number; results: UploadResult[] }Use url in <img> or <video> tags. Short URLs like /api/f/{id} redirect to the file.
Dashboard integration
Use this in your Next.js dashboard — only BUCKET_API_KEY is required.
What you configure
| Item | Required? |
|------|-----------|
| BUCKET_API_KEY in .env.local | Yes |
| Storage / backend URL env vars | No — built into the package |
Checklist
- [ ]
npm install next-media-uploader - [ ] Add
BUCKET_API_KEYto.env.local - [ ] Create
app/api/upload/bucket/route.ts(see Quick start above) - [ ] Add
<Uploader provider="bucket" />to your dashboard - [ ] Save
result.urlon success - [ ] Restart dev server after adding the API key
Dashboard patterns
Project images (max 5 MB, multiple at once)
<Uploader
provider="bucket"
accept="image"
multiple
maxSize={5 * 1024 * 1024}
onBatchSuccess={(batch) =>
setImages((prev) => [...prev, ...batch.results.map((r) => r.url)])
}
/>Project images (one at a time)
<Uploader
provider="bucket"
accept="image"
maxSize={5 * 1024 * 1024}
onSuccess={(r) => setImages((prev) => [...prev, r.url])}
/>Project video (max 100 MB)
<Uploader
provider="bucket"
accept="video"
maxSize={100 * 1024 * 1024}
onSuccess={(r) => setVideoUrl(r.url)}
/>Form submit — send URLs only
{
"projectImages": ["<url-from-upload-result>"],
"projectVideo": "<url-from-upload-result>"
}Troubleshooting
| Issue | Fix |
|-------|-----|
| Missing Bucket API key | Set BUCKET_API_KEY in .env.local, restart server |
| 403 from API | Regenerate API key in Bucket dashboard |
| File too large | Increase maxSize or check plan limits |
| Preview broken | Confirm upload succeeded; use returned url |
A longer copy-paste guide is also included as INTEGRATION.md inside the installed package (node_modules/next-media-uploader/INTEGRATION.md).
License
MIT
