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

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

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>

Was this page helpful?