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/blobpnpm add files-sdk @vercel/blobyarn add files-sdk @vercel/blobbun add files-sdk @vercel/blobUsage
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
VercelBlobAdapterOptions: Cannot read properties of undefined (reading 'ESNext')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
| Method | Status | Notes |
|---|---|---|
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. |
signedUploadUrl |
❌ | No presigned upload primitive. Use handleUpload() from @vercel/blob/client for browser uploads. |
Private access
| Method | Status | Notes |
|---|---|---|
upload |
✅ | |
download |
✅ | |
delete |
✅ | |
list |
✅ | |
head |
✅ | |
exists |
✅ | |
copy |
✅ | |
url |
❌ | No 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. |
signedUploadUrl |
❌ | No presigned upload primitive. Use handleUpload() from @vercel/blob/client for browser uploads. |