API guide
UploadThing vs Cloudflare R2 vs S3 for Next.js 2026
Compare UploadThing, Cloudflare R2, and Amazon S3 for Next.js file uploads in 2026: current pricing inputs, compatibility boundaries, and migration checks.

Choosing file infrastructure for Next.js means choosing who owns the upload workflow, storage contract, security checks, and cost model. UploadThing provides an application-focused file-route workflow. Cloudflare R2 exposes an S3-compatible subset with different pricing and compatibility boundaries. Amazon S3 provides AWS storage classes and integrations. The right fit depends on the workflow you need to operate.
TL;DR verdict
Use conditional selection criteria:
- Choose UploadThing when its documented file routes, middleware, completion hooks, and Next.js adapters match the application workflow you want.
- Choose R2 when its current pricing and S3-compatible with documented exceptions interface fit the required operations.
- Choose S3 when the required storage classes and AWS integrations are part of the design.
Compare documented compatibility and required features first, then recalculate every scenario from current inputs for the actual workload.
API fit matrix
| Decision area | UploadThing | Cloudflare R2 | Amazon S3 |
|---|---|---|---|
| Next.js integration | Documented Next.js App Router adapters and file routes | AWS SDK configured for the R2 endpoint | AWS SDK configured for the target AWS region |
| Upload authorization | File-route middleware | Application issues constrained direct-upload credentials or URLs | Application issues constrained direct-upload credentials or URLs |
| Completion workflow | Upload-completion hook | Application completes and records the object workflow | Application completes and records the object workflow |
| Multipart control | Verify documented and installed SDK behavior | S3 multipart operations supported | Native multipart operation set documented |
| Storage choice | Managed through the product contract | R2 object storage | S3 storage classes |
| Cost model | Re-open the current plan page | Storage and operation units; internet egress listed as free | Region, class, requests, duration, and transfer path affect total |
UploadThing: application-level file routes
UploadThing documents file routes that define accepted file types and route options. Middleware runs before the upload workflow is authorized, and an upload-completion hook can persist application metadata.
import { createUploadthing, type FileRouter } from 'uploadthing/next';
const f = createUploadthing();
export const uploadRouter = {
imageUploader: f({ image: { maxFileSize: '4MB' } })
.middleware(async ({ req }) => {
const user = await requireUser(req);
return { userId: user.id };
})
.onUploadComplete(async ({ metadata, file }) => {
await saveUpload({ userId: metadata.userId, key: file.key });
}),
} satisfies FileRouter;
Current UploadThing SDKs expose the app-scoped file URL as file.ufsUrl. The official public-file pattern is https://<APP_ID>.ufs.sh/f/<FILE_KEY>. Verify the installed package's returned fields and treat its delivery behavior as part of the product contract.
UploadThing SDKs are open source, but this guide keeps no frozen popularity count. Repository counters are volatile and do not decide architecture fit.
The file-route docs include option-specific history labels, such as features introduced in a particular release. Treat that as a feature-level version annotation and verify installed package version before using the option. The package does not freeze an overall SDK release.
Auth and upload matrix
| Stage | UploadThing | R2 or S3 | Required control |
|---|---|---|---|
| Request starts | File-route middleware checks application identity | Server checks identity before issuing upload authority | Bind user and tenant before creating an upload |
| Upload authority | Product workflow returns the allowed upload path | Presigned operation or temporary credential | Limit key, method, content type, size, and expiry where supported |
| Client sends bytes | Product adapter follows the route contract | Client uploads to object storage | Never expose long-lived storage credentials |
| Completion | Completion hook updates application state | Client/server completes and records the object | Verify ownership and final object state |
| Cleanup | Follow product lifecycle behavior | Abort or expire incomplete work | Reconcile abandoned uploads and application records |
R2: compatible surface with explicit boundaries
Cloudflare documents the account endpoint as:
https://<ACCOUNT_ID>.r2.cloudflarestorage.com
For AWS SDK configuration, the region is auto. R2 implements the S3 API with documented additions and omissions, so review the compatibility page for every operation, header, checksum, and feature the application uses.
import { S3Client } from '@aws-sdk/client-s3';
const r2 = new S3Client({
region: 'auto',
endpoint: r2EndpointFromTrustedConfig,
credentials: r2CredentialsFromServerOnlyConfig,
});
The endpoint and credentials belong in trusted server configuration. Never accept an account endpoint or bucket destination directly from an untrusted client.
SDK quality table
| Concern | UploadThing | R2 | S3 |
|---|---|---|---|
| Next.js adapter | First-party documented adapter | AWS SDK in a Next.js server route | AWS SDK in a Next.js server route |
| Type flow | File-router types connect route and client | Application owns request/response types | Application owns request/response types |
| Error access | Verify adapter error surface | Preserve SDK status and request metadata | Preserve SDK status and request metadata |
| Multipart upload | Verify exact SDK/product behavior | Review supported multipart operations | Review documented multipart operations |
| Upgrade gate | Check option annotations and package version | Recheck compatibility page | Recheck SDK and service documentation |
Current pricing inputs
Cloudflare's R2 Standard pricing page, accessed 2026-08-22, lists:
- storage at $0.015 per GB-month;
- Class A operations at $4.50 per million requests;
- internet egress as free; and
- a monthly Standard free tier of 10 GB-month, 1 million Class A requests, and 10 million Class B requests.
These are inputs, not a finished application bill. Operation mix, storage duration, transformations, delivery products, taxes, and other services can change the total.
UploadThing's official pricing page currently renders free, 100GB, and usage-based plan rows. Because these inputs are volatile, reopen the page and date any copied figure; this guide does not freeze UploadThing scenario totals. S3 pricing varies by region, storage class, request class, storage duration, and transfer path. Recalculate every scenario from the current official pages rather than carrying forward old sample totals.
Quota and capacity box
Before implementation, record:
- maximum object and request sizes required by the product;
- expected object count and storage duration;
- upload and download request mix;
- multipart thresholds and part limits;
- concurrency and retry behavior;
- expiration and cleanup rules; and
- any account or plan quotas confirmed in the current console or documentation.
Do not infer capacity from a marketing label; verify feature availability before implementation.
Multipart workflows
R2 supports multipart upload operations through its documented S3-compatible surface. Amazon S3 documents create, upload-part, complete, abort, and list operations. A production workflow should persist the upload identifier, validate part numbers and ETags, complete explicitly, and abort incomplete multipart uploads after the application's chosen retention window.
interface MultipartSession {
ownerId: string;
objectKey: string;
uploadId: string;
createdAt: string;
}
Large-file design also needs retry and integrity policy, so measure with the target workload instead of publishing setup-time or latency claims.
Integration risk box
| Risk | Consequence | Control |
|---|---|---|
| S3-compatible assumption exceeds documented subset | Migration or runtime failure | Inventory every required operation and header |
| Presigned authority is too broad | Unauthorized writes or unexpected cost | Bind object key, expiry, content constraints, and identity |
| Database row is created before object completion | Broken application references | Use pending state and completion reconciliation |
| Incomplete multipart sessions accumulate | Storage and operational residue | Schedule explicit abort and audit |
| Price model is copied from an old article | Incorrect purchasing decision | Recalculate every scenario from live inputs |
| Application cannot change providers | Migration becomes a product rewrite | Store provider-neutral object identity and metadata |
Migration decision: S3 to R2
The AWS SDK can target R2's documented endpoint, but a safe migration is a compatibility review, not a string substitution.
- Inventory APIs, headers, checksums, event flows, lifecycle rules, storage classes, and delivery paths in use.
- Compare each requirement against the current R2 compatibility page.
- Create the destination bucket and server-side credentials.
- Copy a representative object set and verify metadata and checksums.
- Dual-read or stage a controlled cutover if the application requires it.
- Update public delivery and cache behavior separately from storage writes.
- Reconcile object counts and application records.
- Keep rollback criteria until new writes and reads are verified.
Cloudflare's S3 compatibility page had a current documentation date of July 31, 2026 and states that implementation is still in progress. The pricing page was updated August 7, 2026. Recheck both before a migration.
Source-backed evidence
UploadThing
Official docs support Next.js adapters, file routes, middleware, completion hooks, and option-level history. The official repository supports SDK source provenance.
Cloudflare R2
Official docs support the current pricing units, endpoint form, auto region, compatibility caveats, and multipart operations.
Amazon S3
Official pages support the storage-class, pricing-variable, and multipart-operation distinctions used here.
Editorial limits
The matrix, migration checklist, and security controls are implementation guidance. Validate them in the target account, package version, workload, and threat model.
Methodology
APIScout reviewed the twelve official sources below on 2026-08-22. Unsupported comparative totals, popularity counters, setup times, latency and savings claims, storage-internal assumptions, and undocumented product limits were removed. Pricing and availability remain volatile.
Source-backed FAQ
Is R2 interchangeable with S3?
Not without review. R2 implements an S3-compatible surface with documented omissions and additions. Compare the exact operations and headers your application uses.
Which option has the lowest cost?
That depends on storage duration, requests, transfer path, product plan, and adjacent services. Use current account-specific inputs and recalculate costs from current inputs.
Does UploadThing remove application authorization work?
No. Its middleware gives the application a place to enforce identity and policy. The application still owns user, tenant, and business authorization.
What should a multipart design retain?
Retain ownership, object key, upload identifier, completed parts, expiry, and cleanup state. Test completion, retry, and abort paths.
Sources
- Cloudflare R2 pricing — accessed 2026-08-22
- Cloudflare R2 S3 API compatibility — accessed 2026-08-22
- Cloudflare R2 multipart objects — accessed 2026-08-22
- Amazon S3 pricing — accessed 2026-08-22
- Amazon S3 storage classes — accessed 2026-08-22
- Amazon S3 multipart upload — accessed 2026-08-22
- UploadThing documentation — accessed 2026-08-22
- UploadThing: Next.js App Router setup — accessed 2026-08-22
- UploadThing file routes — accessed 2026-08-22
- UploadThing pricing — accessed 2026-08-22
- UploadThing: Working with Files — accessed 2026-08-22
- UploadThing repository — accessed 2026-08-22
Compare the storage APIs directly on APIScout.
Related guides
{/* Sources: file-r2-multipart, file-r2-pricing, file-r2-s3, file-s3-classes, file-s3-multipart, file-s3-pricing, file-uploadthing-docs, file-uploadthing-nextjs-app-router, file-uploadthing-pricing, file-uploadthing-repo, file-uploadthing-routes, file-uploadthing-working-files. Claims: apiscout:uploadthing-vs-cloudflare-r2-vs-s3-nextjs-2026:pricing_or_plan, apiscout:uploadthing-vs-cloudflare-r2-vs-s3-nextjs-2026:downloads_stars_forks, apiscout:uploadthing-vs-cloudflare-r2-vs-s3-nextjs-2026:release_version_status, apiscout:uploadthing-vs-cloudflare-r2-vs-s3-nextjs-2026:compatibility_integrations, apiscout:uploadthing-vs-cloudflare-r2-vs-s3-nextjs-2026:product_capabilities, apiscout:uploadthing-vs-cloudflare-r2-vs-s3-nextjs-2026:performance_benchmarks, apiscout:uploadthing-vs-cloudflare-r2-vs-s3-nextjs-2026:ranking_popularity_superlative, apiscout:uploadthing-vs-cloudflare-r2-vs-s3-nextjs-2026:availability_or_provider_status. */}
The API Integration Checklist (Free PDF)
Step-by-step checklist: auth setup, rate limit handling, error codes, SDK evaluation, and pricing comparison for 50+ APIs. Used by 200+ developers.
Join 200+ developers. Unsubscribe in one click.