Vercel Blob

Vercel Blob. Prefers auto-rotating Vercel OIDC (VERCEL_OIDC_TOKEN + BLOB_STORE_ID), falls back to BLOB_READ_WRITE_TOKEN, or pass credentials manually.

Installation

@vercel/blob is an optional peer dependency of files-sdk - install alongside the SDK so the adapter's imports resolve at runtime.

npm install files-sdk @vercel/blob

Usage

On Vercel, the adapter prefers Vercel's OIDC authentication when VERCEL_OIDC_TOKEN and BLOB_STORE_ID are present (both are auto-injected when the Blob store is connected to the project). OIDC tokens rotate automatically, so they remove the risk that a long-lived secret leaks from the codebase or environment. Off Vercel - or if OIDC isn't configured - the adapter falls back to BLOB_READ_WRITE_TOKEN. An explicit token option always wins.

import { Files } from "files-sdk";
import { vercelBlob } from "files-sdk/vercel-blob";

// On Vercel: VERCEL_OIDC_TOKEN + BLOB_STORE_ID are auto-injected when the
// Blob store is connected to the project (OIDC, recommended). Off Vercel,
// or as a fallback, BLOB_READ_WRITE_TOKEN is used.
const files = new Files({ adapter: vercelBlob() });

Pass oidcToken and storeId directly for runtimes that don't expose process.env (Vite, etc.), or to bypass env detection entirely:

// Frameworks that don't load .env.local into process.env (Vite, etc.)
// need OIDC credentials passed explicitly.
const files = new Files({
  adapter: vercelBlob({
    oidcToken: loadOidcToken(),
    storeId: loadStoreId(),
  }),
});

downloadTimeoutMs bounds the public-URL fetches issued by download() and the lazy bodies returned from head()/list(). Defaults to 5 minutes; pass 0 to disable. A hung CDN response would otherwise leak a fetch that never resolves.

access selects public or private blobs and is fixed at construction. Default "public" matches the existing behavior. With access: "private", uploads use Vercel's private mode and reads route through blob.get() with whichever credentials the adapter resolved (OIDC or read-write token) instead of a public URL fetch - there is no permanent public URL for private blobs, so url() throws. Need both? Use two adapters.

Options

Prop

Type

Limitations

User metadata isn't supported by the underlying API, so passing a non-empty metadata throws rather than silently dropping it. cacheControl is supported (it maps to the blob's cacheControlMaxAge).

Compatibility

Public access

MethodStatusNotes
upload
download
delete
list
search
head
exists
copy
url⚠️Returns the permanent CDN URL. expiresIn is silently ignored (no signing primitive); responseContentDisposition throws (no Content-Disposition override available). Use a different provider for buckets with untrusted user-uploaded content.
signedUploadUrlNo presigned upload primitive. Use handleUpload() from @vercel/blob/client for browser uploads.

Private access

MethodStatusNotes
upload
download
delete
list
head
exists
copy
urlNo URL primitive for private blobs - the underlying SDK requires an authenticated blob.get() call with the token. Use download() instead, or instantiate a second public-access adapter.
signedUploadUrlNo presigned upload primitive. Use handleUpload() from @vercel/blob/client for browser uploads.

On this page