0.1.0 • Published yesterday
next-media-uploader
Licence
MIT
Version
0.1.0
Deps
2
Size
247 kB
Vulns
0
Weekly
0
next-media-uploader
Media uploads for Next.js — switch between Backblaze B2 and Supabase Storage with one API.
Install
npm install next-media-uploader
For Supabase uploads, also install peer dependencies:
npm install @supabase/supabase-js tus-js-client
Choose a provider
Set the default provider in .env.local:
NEXT_PUBLIC_UPLOAD_PROVIDER=b2 # or "supabase"
Or pass provider per component:
useUpload({ provider: "b2" })
useUpload({ provider: "supabase" })
| Provider | How it uploads | Best for |
|---|---|---|
b2 |
Server-side via API route | Private buckets, B2 credentials stay on server |
supabase |
Client-side TUS resumable upload | Large files, bypasses API body limits |
Environment variables
Backblaze B2 (server-only)
B2_KEY_ID=your_key_id
B2_APPLICATION_KEY=your_application_key
B2_BUCKET_NAME=your_bucket_name
B2_REGION=us-east-005
B2_BUCKET_PUBLIC=false
Supabase (browser-safe)
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
NEXT_PUBLIC_SUPABASE_IMAGE_BUCKET=images
NEXT_PUBLIC_SUPABASE_VIDEO_BUCKET=videos
Set both bucket vars to the same name if using one bucket for everything.
Quick start
Backblaze B2
1. API route
// app/api/upload/route.ts
import { createUploadRoute } from "next-media-uploader/next";
export const POST = createUploadRoute();
2. React hook
"use client";
import { useUpload } from "next-media-uploader/react";
const { upload, isUploading, url } = useUpload({
provider: "b2",
accept: "both",
maxSize: 50 * 1024 * 1024,
});
Supabase
No API route needed — uploads go directly to Supabase via TUS.
"use client";
import { useUpload } from "next-media-uploader/react";
const { upload, progress } = useUpload({
provider: "supabase",
accept: "image",
maxSize: 5 * 1024 * 1024,
bucket: "images", // optional — auto-picked from accept
});
Custom UI
Three levels of customization:
1. Default design (zero config)
<Uploader provider="b2" accept="image" maxSize={5 * 1024 * 1024} />
2. Slots — override individual parts
<Uploader
provider="b2"
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>
),
}}
/>
Available slots: trigger, progress, error, preview
3. Children — full control
import { Uploader, UploaderInput } from "next-media-uploader/react";
<Uploader provider="supabase" accept="video" maxSize={100 * 1024 * 1024}>
{({ openFilePicker, isUploading, progress, error, result, inputRef, getInputProps }) => (
<div className="your-custom-design">
<button onClick={openFilePicker} disabled={isUploading}>
{isUploading ? `Uploading ${Math.round(progress * 100)}%` : "Pick video"}
</button>
<UploaderInput inputRef={inputRef} getInputProps={getInputProps} />
{error && <p>{error}</p>}
{result && <video src={result.url} controls />}
</div>
)}
</Uploader>
API reference
| Export | From | Description |
|---|---|---|
useUpload(options) |
next-media-uploader/react |
Hook — provider, accept, maxSize, custom UI |
Uploader |
next-media-uploader/react |
Render-props upload component |
createUploadRoute() |
next-media-uploader/next |
B2 API route factory |
createB2Client() |
next-media-uploader/server |
B2 S3 client |
uploadFile() |
next-media-uploader/server |
Server-side B2 upload |
uploadToSupabase() |
next-media-uploader/supabase |
Direct TUS upload |
deleteFromSupabase() |
next-media-uploader/supabase |
Delete by URL |
useUpload / Uploader options
| Option | Type | Default | Description |
|---|---|---|---|
provider |
"b2" | "supabase" |
env or "b2" |
Storage provider |
accept |
"image" | "video" | "both" |
"both" |
Allowed file types |
maxSize |
number |
— | Max bytes |
bucket |
"images" | "videos" |
auto | Supabase bucket (images/videos) |
uploadUrl |
string |
"/api/upload" |
B2 API endpoint |
onProgress |
(0–1) => void |
— | Upload progress |
Supabase setup
- Create bucket(s) in Supabase Storage (public for
getPublicUrlpreviews) - Add storage policies for
anonINSERT and DELETE - TUS endpoint is used automatically:
{SUPABASE_URL}/storage/v1/upload/resumable
See SUPABASE_MEDIA_UPLOAD.md for full setup details.
License
MIT