Getting Started with SvelteKit
Package Setup
Install the packages
npm install uploadthing @uploadthing/svelte
Add env variables
UPLOADTHING_TOKEN=... # A token for interacting with the SDK
If you don't already have a uploadthing secret key, sign up ↗ and create one from the dashboard! ↗
Set Up A FileRouter
Creating your first FileRoute
All files uploaded to uploadthing are associated with a FileRoute. The following is a very minimalistic example, with a single FileRoute "imageUploader". Think of a FileRoute similar to an endpoint, it has:
- Permitted types ["image", "video", etc]
- Max file size
- (Optional)
middleware
to authenticate and tag requests onUploadComplete
callback for when uploads are completed
To get full insight into what you can do with the FileRoutes, please refer to the File Router API.
src/lib/server/uploadthing.ts
import { createUploadthing } from "uploadthing/server";
import type { FileRouter } from "uploadthing/server";
const f = createUploadthing();
const auth = (req: Request) => ({ id: "fakeId" }); // Fake auth function
// FileRouter for your app, can contain multiple FileRoutes
export const ourFileRouter = {
// Define as many FileRoutes as you like, each with a unique routeSlug
imageUploader: f({ image: { maxFileSize: "4MB" } })
// Set permissions and file types for this FileRoute
.middleware(async ({ req }) => {
// This code runs on your server before upload
const user = await auth(req);
// If you throw, the user will not be able to upload
if (!user) throw new Error("Unauthorized");
// Whatever is returned here is accessible in onUploadComplete as `metadata`
return { userId: user.id };
})
.onUploadComplete(async ({ metadata, file }) => {
// This code RUNS ON YOUR SERVER after upload
console.log("Upload complete for userId:", metadata.userId);
console.log("file url", file.url);
}),
} satisfies FileRouter;
export type OurFileRouter = typeof ourFileRouter;
Create an API route using the FileRouter
File path here doesn't matter, you can serve this from any route. We recommend
serving it from /api/uploadthing
.
src/routes/api/uploadthing/+server.ts
import { env } from "$env/dynamic/private";
import { ourFileRouter } from "$lib/server/uploadthing";
import { createRouteHandler } from "uploadthing/server";
const handlers = createRouteHandler({
router: ourFileRouter,
config: {
token: env.UPLOADTHING_TOKEN,
},
});
export { handlers as GET, handlers as POST };
See configuration options in server API reference
Creating the UploadThing Helpers
Generating the createUploadThing
helper function lets you create your own
components, with full type safety:
src/lib/utils/uploadthing.ts
import type { OurFileRouter } from "$lib/server/uploadthing";
import { generateSvelteHelpers } from "@uploadthing/svelte";
export const { createUploader, createUploadThing } =
generateSvelteHelpers<OurFileRouter>();
Add UploadThing's Styles
Wrap your Tailwind config with the withUt
helper. You can learn more about our
Tailwind helper in the "Theming" page
import { withUt } from "uploadthing/tw";
export default withUt({
// Your existing Tailwind config
content: ["./src/**/*.{ts,tsx,mdx}"],
...
});
Use the FileRouter in your app
The @uploadthing/svelte
package includes an "Uploader" component that you can
simply drop into your app, and start uploading files immediately. To create the
"Uploader" props you can use the "createUploader" helper you generated in the
previous step.
src/routes/+page.svelte
<script lang="ts">
import { createUploader } from "$lib/utils/uploadthing";
import { Uploader } from "@uploadthing/svelte";
const uploader = createUploader("imageUploader", {
onClientUploadComplete: (res) => {
console.log(`onClientUploadComplete`, res);
alert("Upload Completed");
},
onUploadError: (error: Error) => {
alert(`ERROR! ${error.message}`);
},
});
</script>
<main>
<Uploader {uploader} />
</main>
You can also choose to use the "UploadButton" and "UploadDropzone" components
separately. Both are included in the @uploadthing/svelte
package.
src/routes/+page.svelte
<script lang="ts">
import { createUploader } from "$lib/utils/uploadthing";
import { Uploader } from "@uploadthing/svelte";
const uploader = createUploader("imageUploader", {
onClientUploadComplete: (res) => {
console.log(`onClientUploadComplete`, res);
alert("Upload Completed");
},
onUploadError: (error: Error) => {
alert(`ERROR! ${error.message}`);
},
});
</script>
<div class="ut-flex ut-flex-col ut-items-center ut-justify-center ut-gap-4">
<span class="ut-text-center ut-text-4xl ut-font-bold">
{`Upload a file using a button:`}
</span>
<UploadButton {uploader} />
</div>
<div class="ut-flex ut-flex-col ut-items-center ut-justify-center ut-gap-4">
<span class="ut-text-center ut-text-4xl ut-font-bold">
{`...or using a dropzone:`}
</span>
<UploadDropzone {uploader} />
</div>
Advanced usage: createUploadThing
For advanced use cases, the premade components might not be flexible enough to
suit your needs. In that case, you can use the createUploadThing
helper
function to build your own components with a custom upload flow:
Create the createUploadThing
helper
First, generate a typed helper using the generateSvelteHelpers
function from
@uploadthing/svelte
as show above.
Use the helper
src/routes/+page.svelte
<script lang="ts">
import { createUploadThing } from "$lib/utils/uploadthing";
const { startUpload } = createUploadThing("imageUploader", {
onClientUploadComplete: () => {
alert("Upload Completed");
},
});
</script>
<input
type="file"
on:change={async (e) => {
const file = e.currentTarget.files?.[0];
if (!file) return;
// Do something with files
// Then start the upload
await startUpload([file]);
}}
/>
Theming Svelte components
Check out our theming guide to know the basics about
components anatomy and
theming props as they still apply to
@uploadthing/svelte
.
However, content customization makes use of Svelte's named slots and slot props.
UploadButton content customization
<UploadButton {uploader}>
<span slot="button-content" let:state>
{state.isUploading ? "Uploading..." : "Pick a file"}
</span>
<span slot="clear-btn" let:state>
Clear files
</span>
<span slot="allowed-content" let:state>
You can choose between {state.fileTypes.join(", ")} files
</span>
</UploadButton>
UploadDropzone content customization
<UploadDropzone {uploader}>
<i slot="upload-icon" let:state>
<!-- some custom icon -->
</i>
<span slot="button-content" let:state>
{state.isUploading ? "Uploading..." : "Pick a file"}
</span>
<span slot="label" let:state>
{state.ready ? "Ready to upload" : "Loading..."}
</span>
<span slot="allowed-content" let:state>
You can choose between {state.fileTypes.join(", ")} files
</span>
</UploadDropzone>