Skip to content
Files SDK
Esc
↑↓navigate↵open⌘Jpreview
On this page

UploadThing

UploadThing via the uploadthing/server SDK. Auto-loads UPLOADTHING_TOKEN and maps your keys onto UploadThing's customId so operations route by your key.

Installation

uploadthing is an optional peer dependency of files-sdk - install alongside the SDK so the adapter’s imports resolve at runtime.

npm install files-sdk uploadthing
pnpm add files-sdk uploadthing
yarn add files-sdk uploadthing
bun add files-sdk uploadthing
nub add files-sdk uploadthing
aube add files-sdk uploadthing

Usage

UploadThing via the official uploadthing/server SDK. UploadThing generates its own internal file keys, so the adapter maps the user-supplied key onto UploadThing’s customId with defaultKeyType: "customId" - every subsequent operation routes by your key, not the auto-generated one.

import { Files } from "files-sdk";
import { uploadthing } from "files-sdk/uploadthing";

// UPLOADTHING_TOKEN is auto-loaded from env. The token is a base64
// JSON of { apiKey, appId, regions[] } - the adapter decodes it at
// construction so url() can synthesize the public CDN URL and
// signedUploadUrl() can sign a UFS PUT URL without an API round trip.
const files = new Files({
  adapter: uploadthing({
    // acl: "public-read",       // default; switch to "private" to mint
    //                            // signed URLs through generateSignedURL
    // slug: "mediaUploader",    // required only for signedUploadUrl()
  }),
});

Options

PropType
token?string

UploadThing token. Falls back to `process.env.UPLOADTHING_TOKEN`. Tokens are base64-encoded JSON of the form `{ apiKey, appId, regions: string[] }` — the adapter decodes them at construction time so it can compute the public CDN host (`{appId}.ufs.sh`) and sign UFS presigned PUT URLs without an API round-trip. A token that doesn't decode to that shape throws immediately rather than failing later on the first call.

Typestring
acl?"public-read" | "private"

ACL applied to uploads. Drives both the upload-time ACL and `url()` behavior — `"public-read"` returns the permanent CDN URL, `"private"` mints a short-lived signed URL via `generateSignedURL`. Defaults to `"public-read"`, which matches UploadThing's most common use case. Fixed at construction so a single `Files` instance is unambiguously one or the other. If you need both, instantiate two adapters.

Type"public-read" | "private"
slug?string

UploadThing file-router slug. Required only by `signedUploadUrl()`, which embeds it as `x-ut-slug` on the ingest URL — UploadThing validates the upload against the route's config (allowed file types/sizes). Server-side `upload()` does not need it.

Typestring
defaultUrlExpiresIn?number

Default expiry (seconds) for signed download URLs and for `url()` when `acl` is `"private"`. UploadThing caps signed URLs at 7 days. Defaults to 3600 (1 hour).

Typenumber
downloadTimeoutMs?number

Timeout in milliseconds for the HEAD/GET fallbacks that `head()`, `download()`, and lazy bodies returned from `list()` issue against the file URL. A hung CDN response would otherwise leak a fetch that never resolves. Defaults to 300_000 (5 minutes). Pass `0` to disable.

Typenumber
region?string

Override the region alias used to construct the ingest URL for `signedUploadUrl()`. Defaults to the first region in the decoded token, or `"sea1"` if none is present.

Typestring

Compatibility

Method Status Notes
upload ⚠️ Stream bodies are buffered up-front - uploadFiles takes a Blob, so streaming uploads can’t avoid materializing the body in memory. User metadata and cacheControl aren’t supported by the UFS API, so passing either throws rather than silently dropping it. Resumable uploads (control) are not supported — UploadThing manages chunking server-side and exposes no resumable session.
download ✅
delete ✅
list ⚠️ UploadThing’s listFiles is offset/limit, not cursor-based - the adapter encodes the next offset as a numeric cursor. prefix is unsupported server-side; the adapter filters the returned page client-side, which under-returns when the prefix isn’t satisfied within a single page.
search ⚠️ Built on listAll — inherits this adapter’s list behavior above. Client-side key match (glob, regex, substring, exact).
head ⚠️ UploadThing has no metadata endpoint, so head() issues a HEAD request against the resolved file URL (signed for private, CDN for public) and parses size/content-type/etag/last-modified from the response headers. User metadata isn’t supported.
exists ⚠️ UploadThing has no metadata endpoint, so exists() issues a HEAD request against the resolved file URL (signed for private, CDN for public) and treats 404 as false.
copy ⚠️ Read-then-write - UploadThing has no server-side copy primitive, so the source is downloaded and re-uploaded. Costs an egress + an ingest; not atomic.
url ⚠️ Public adapters return the permanent CDN URL https://{appId}.ufs.sh/f/{key} and silently ignore expiresIn. Private adapters mint a signed read URL via generateSignedURL (1-hour default, 7-day maximum - a longer expiresIn throws; capabilities.signedUrl.maxExpiresIn reports the cap, and the gateway clamps to it). responseContentDisposition throws either way - UploadThing has no Content-Disposition override on signed or CDN URLs. Use a private adapter for buckets with untrusted user-uploaded content.
signedUploadUrl ⚠️ PUT URL only - built against UploadThing’s UFS ingest endpoint with an HMAC-SHA256 signature over the URL. maxSize throws - the URL signature can’t carry a server-enforced size cap; enforce caps in the file-router config tied to the adapter’s slug. A positive minSize throws (no equivalent on UFS); minSize: 0 is accepted. The user-supplied key is bound as x-ut-custom-id so subsequent ops can route by it.

Was this page helpful?