Skip to main content
Upstash Blob is S3-compatible object storage. @upstash/blob has a Bucket client for your server, and an upload handler plus React hooks that upload from the browser straight to storage. This page builds a working file picker on Next.js App Router. Other frameworks work the same way; see Other frameworks.

Setup

Create a bucket in the Upstash Console and put its token in your environment.
.env
The console asks whether the bucket is public or private:
  • Public: every object has a public URL. For avatars, product images, anything a page links to directly.
  • Private: no public URL. Every read goes through a time-limited signed URL. For user documents, invoices, anything that must not be guessable.

Upload from your server

lib/reports.ts
Bytes already on your server go to the bucket with put. blob.url is the public object URL, undefined on a private bucket. See Writing.

Upload from the browser

Files a user picks go straight from the browser to storage. Your server only authorizes the upload and records the result, so the bytes never pass through it.
1

Write the upload handler

lib/uploads.ts
The handler runs on your server. It decides who may upload, where the object goes, and what happens once it lands. It never sees the bytes.This one accepts anyone. Upload handler adds the auth check and the completion callback.
2

Mount it as a route

app/api/upload/route.ts
POST runs the upload and GET serves the route’s constraints. The hooks look at /api/upload by default, so nothing else has to name a URL.
3

Bind the hooks to the handler

lib/upload-hooks.ts
uploadHooks<typeof uploads>() reads the handler’s type, so route names and completion data are checked at compile time. The import type is erased at build time and never pulls server code into the browser bundle.
4

Upload a file

app/page.tsx
You get these without more code:
  • Multipart for large files. Past 16 MB the SDK switches to parts, with pause, resume and per-part retry. See Large files.
  • Retries. Failed parts back off and retry, and an expired signature is refreshed mid-upload.
  • A picker that matches the server. accept comes from the route’s own GET, so an oversized file is refused before any request goes out. See Constraints.
  • Progress. percent, status and pending read the same for one PUT or 200 parts.
  • Types end to end. Whatever onUploadComplete returns is upload.blob.data on the client.

Next steps

Upload handler

Auth, routes, and the completion callback in full.

Upload client

useUpload, the record it renders, and the non-React client.

Writing

put, metadata, conditional writes and multipart from the server.

Recipes

Avatars, attachments and private documents, wired end to end.