File uploads for SvelteKit
Upload files from SvelteKit directly to your storage bucket. A +server.ts endpoint signs a presigned PUT URL and the browser sends the file to R2 or S3. UploadKit has no Svelte components yet, so this page shows the server-side flow that works today.
What is supported for SvelteKit
UploadKit does not ship Svelte components. The UI components in @uploadkitdev/react are React components.
For SvelteKit you get a server route and a framework-agnostic client. The CLI's SvelteKit setup installs @uploadkitdev/core and creates server files only. The starter template goes one step further and shows the raw presigned URL flow with no UploadKit package at all: a +server.ts endpoint signs, and a Svelte 5 page uploads with fetch. Full Svelte components are listed in the repository as a future release, with no date.
Presigned uploads in a SvelteKit app
These files come from the create-uploadkit-app SvelteKit template.
1. Scaffold the app
npx create-uploadkit-app my-app --template sveltekit2. Add storage credentials
R2_ACCOUNT_ID=
R2_ACCESS_KEY_ID=
R2_SECRET_ACCESS_KEY=
R2_BUCKET=3. Sign uploads in a server route
The endpoint reads credentials from $env/dynamic/private, so they stay on the server. In the template it lives at src/routes/api/sign/+server.ts.
import { json, type RequestHandler } from '@sveltejs/kit';
import { env } from '$env/dynamic/private';
import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
export const POST: RequestHandler = async ({ request }) => {
const raw = await request.json().catch(() => null);
if (!raw || typeof raw.filename !== 'string' || typeof raw.contentType !== 'string') {
return json({ error: 'Body must be { filename: string, contentType: string }' }, { status: 400 });
}
const client = new S3Client({
region: 'auto',
endpoint: `https://${env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: env.R2_ACCESS_KEY_ID,
secretAccessKey: env.R2_SECRET_ACCESS_KEY,
},
});
const key = `${crypto.randomUUID()}-${raw.filename}`;
// ContentType MUST be set on both sign AND the client PUT — mismatch → 403.
const url = await getSignedUrl(
client,
new PutObjectCommand({
Bucket: env.R2_BUCKET,
Key: key,
ContentType: raw.contentType,
}),
{ expiresIn: 60 * 5 },
);
return json({ url, key, method: 'PUT' as const });
};4. Upload from the page
The page requests a URL, then sends the file to storage with a PUT that uses the same content type you signed.
<script lang="ts">
let file = $state<File | null>(null);
let status = $state<'idle' | 'uploading' | 'done' | 'error'>('idle');
async function upload() {
if (!file) return;
status = 'uploading';
try {
const signResponse = await fetch('/api/sign', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ filename: file.name, contentType: file.type || 'application/octet-stream' }),
});
const sign = await signResponse.json();
if ('error' in sign) throw new Error(sign.error);
const put = await fetch(sign.url, {
method: 'PUT',
body: file,
headers: { 'content-type': file.type || 'application/octet-stream' },
});
if (!put.ok) throw new Error(`Upload failed: ${put.status} ${put.statusText}`);
status = 'done';
} catch {
status = 'error';
}
}
</script>
<input type="file" onchange={(e) => (file = e.currentTarget.files?.[0] ?? null)} />
<button type="button" onclick={upload} disabled={!file || status === 'uploading'}>Upload</button>What this setup gives you
Same primitives as the rest of the SDK, without a UI layer on the Svelte side.
Presigned direct-to-storage uploads
The SvelteKit server never handles file bytes. It signs a short-lived URL and the browser uploads straight to the bucket.
Bring your own storage
Cloudflare R2 in the template, and the same AWS SDK code works against other S3-compatible buckets.
Framework-agnostic core
@uploadkitdev/core has no React dependency, so it can be used from any JavaScript environment.
React components, if you need them
Teams that already render React islands can use @uploadkitdev/react. UploadKit does not provide a Svelte wrapper.
The tutorial file uploads in SvelteKit with presigned URLs adds hooks.server.ts authentication, zod validation and a Svelte 5 dropzone on top of this flow. If you are choosing between a signed URL and a proxy, read presigned URLs vs server proxy uploads.
Limits per plan
These apply when you use UploadKit managed storage. Full details are on the pricing page.
Free
5 GB storage, 2 GB bandwidth per month, 4 MB max file, 1,000 uploads per month, 2 projects.
Pro — $15/mo
100 GB storage, 200 GB bandwidth, 512 MB max file, 50,000 uploads per month, 10 projects.
Team — $35/mo
1 TB storage, 2 TB bandwidth, 5 GB max file, 500,000 uploads per month, 50 projects.
Enterprise
Custom limits. See the pricing page.
Frequently asked questions
Are there UploadKit components for Svelte?
Not yet. The UploadKit components are React components. For SvelteKit the starter template builds its own small Svelte 5 page and uses a server endpoint that returns a presigned URL. The repository marks full Svelte components as a future release.
What does npx uploadkit init add to a SvelteKit project?
It detects @sveltejs/kit, installs only @uploadkitdev/core, creates src/routes/api/uploadkit/[...uploadkit]/+server.ts and src/lib/uploadkit.ts, and adds UPLOADKIT_API_KEY to .env. It does not touch any .svelte file and it adds no React provider. Treat the generated files as a starting point and verify them before relying on them.
Can I use the React components inside SvelteKit?
The create-uploadkit-app SvelteKit template contains a placeholder dropzone file that wraps the React components, labelled as a stand-in until Svelte components ship. It is not used by the template page, which uses plain Svelte instead. Mixing React into a Svelte app needs extra setup that UploadKit does not provide.
Do uploads go through my SvelteKit server?
No. The +server.ts endpoint only returns a presigned PUT URL. The browser then sends the file straight to your bucket with fetch. The content type must be the same on the signing call and the PUT, otherwise the storage provider answers 403. Presigned URLs vs server proxy
Which storage can I use?
The template signs against Cloudflare R2 and the same code works with any S3-compatible bucket through the AWS SDK. For the managed option, sign up for an UploadKit project. BYOS docs
What does it cost?
The free plan includes 5 GB storage, 2 GB bandwidth per month, a 4 MB maximum file size and 1,000 uploads per month. Pro is $15 per month and Team is $35 per month. See pricing
Add uploads to your SvelteKit app
Start on the free plan: 5 GB storage, 2 GB bandwidth per month, no credit card.