Skip to content
Files SDK
Esc
navigateopen⌘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

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 ⚠️ 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). 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 is advisory: UploadThing enforces upload caps via the file-router config tied to the adapter’s slug, not via the URL signature. minSize is ignored (no equivalent on UFS). The user-supplied key is bound as x-ut-custom-id so subsequent ops can route by it.

Was this page helpful?