Getting Started
SolidStart

SolidStart Setup

Package Setup

Install the packages

npm install uploadthing @uploadthing/solid solidjs-dropzone attr-accept

Add env variables

💡

If you don't already have a uploadthing secret key, sign up (opens in a new tab) and create one from the dashboard! (opens in a new tab)

UPLOADTHING_SECRET=... # A secret key for your app (starts with sk_live_)
UPLOADTHING_APP_ID=... # Your app id

Set Up A FileRouter

Creating your first FileRoute

💡

For more details on how to create a file router, see the router docs

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/server/uploadthing.ts
import { createUploadthing, UploadThingError } from "uploadthing/server";
import type { FileRouter } from "uploadthing/server";
 
const f = createUploadthing();
 
const auth = (req: Request) => ({ id: "fakeId" }); // Fake auth function
 
export const uploadRouter = {
  profileImage: f({
    image: {
      maxFileSize: "4MB",
    },
  })
    .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 UploadThingError("Unauthorized");
 
      // Whatever is returned here is accessible in onUploadComplete as `metadata`
      return { userId: user.id };
    })
    .onUploadComplete(({ file, metadata }) => {
      // This code RUNS ON YOUR SERVER after upload
      console.log("Upload complete for userId:", metadata.userId);
 
      console.log("file url", file.url);
 
      // !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback
      return { uploadedBy: metadata.userId };
    }),
} satisfies FileRouter;
 
export type OurFileRouter = typeof uploadRouter;

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.ts
import { createRouteHandler } from "uploadthing/server";
 
import { uploadRouter } from "~/server/uploadthing";
 
export const { GET, POST } = createRouteHandler({
  router: uploadRouter,
 
  // Apply an (optional) custom config:
  // config: { ... },
});

See configuration options in server API reference

Creating The UploadThing Components (recommended)

Generating components let's you pass your generic FileRouter once and then have typesafe components everywhere, instead of having to pass the generic everytime you mount a component, but you can also import the components individually from @uploadthing/solid.

src/utils/uploadthing.ts
import {
  generateUploadButton,
  generateUploadDropzone,
} from "@uploadthing/solid";
 
import type { OurFileRouter } from "~/server/uploadthing";
 
// URL is only needed for server side rendering, or when your router
// is deployed on a different path than `/api/uploadthing`
const url = `http://localhost:${process.env.PORT ?? 3000}`;
 
export const UploadButton = generateUploadButton<OurFileRouter>({ url });
export const UploadDropzone = generateUploadDropzone<OurFileRouter>({ url });

Include our stylesheet

You need to import our styles for the button to look right. Import this in the root.tsx makes it available everywhere.

src/root.tsx
import "@uploadthing/solid/styles.css";
 
export default function Root() {
  ...
}

Use the FileRouter in your app

The @uploadthing/solid package includes an "Uploader" component that you can simply drop into your app, and start uploading files immediately.

src/routes/index.tsx
import { Uploader } from "~/utils/uploadthing";
 
export default function Home() {
  return (
    <main>
      <Uploader
        endpoint="profileImage"
        onClientUploadComplete={(res) => {
          console.log(`onClientUploadComplete`, res);
          alert("Upload Completed");
        }}
        buttonMultiple
      />
    </main>
  );
}

Use the FileRouter in your app

UploadThing provides a few premade components that you can use to upload files in your app. For component customization, see Theming, and for even more advanced use cases check out the hook usage

UploadButton

A simple button that opens the native file picker when clicked. It lists what files are allowed and how many files can be uploaded of that type.

Allowed content
// Or import from your typed, generated components
import { UploadButton } from "@uploadthing/solid";
 
import type { OurFileRouter } from "~/server/uploadthing";
 
export default function Home() {
  return (
    <main>
      <UploadButton<OurFileRouter>
        endpoint="profileImage"
        // needed when server side rendering
        url="http://localhost:3000"
        onClientUploadComplete={() => {
          alert("Upload Completed");
        }}
        onUploadError={(error: Error) => {
          // Do something with the error.
          alert(`ERROR! ${error.message}`);
        }}
      />
    </main>
  );
}

UploadDropzone

This is a SolidJS dropzone component that you can use to upload files, you can drag files into it.

// Or import from your typed, generated components
import { UploadDropzone } from "@uploadthing/solid";
 
import type { OurFileRouter } from "~/server/uploadthing";
 
export default function Home() {
  return (
    <main>
      <UploadDropzone<OurFileRouter>
        endpoint="profileImage"
        // needed when server side rendering
        url="http://localhost:3000"
        onClientUploadComplete={() => {
          alert("Upload Completed");
        }}
        onUploadError={(error: Error) => {
          // Do something with the error.
          alert(`ERROR! ${error.message}`);
        }}
      />
    </main>
  );
}

Uploader

A combination of the UploadButton and UploadDropzone components.

// Or import from your typed, generated components
import { Uploader } from "@uploadthing/solid";
 
import type { OurFileRouter } from "~/server/uploadthing";
 
export default function Home() {
  return (
    <main>
      <Uploader<OurFileRouter>
        endpoint="profileImage"
        // needed when server side rendering
        url="http://localhost:3000"
        onClientUploadComplete={() => {
          alert("Upload Completed");
        }}
      />
    </main>
  );
}

Advanced usage: useUploadThing

For advanced use cases, the premade components might not be flexible enough to suit your needs. In that case, you can use the useUploadThing hook to build your own components with a custom upload flow:

Create the hook

First, generate a typed hook using the generateSolidHelpers function from @uploadthing/solid:

src/utils/uploadthing.ts
import { generateSolidHelpers } from "@uploadthing/solid";
 
import type { OurFileRouter } from "~/server/uploadthing";
 
export const { useUploadThing } = generateSolidHelpers<OurFileRouter>();

Use the hook

Then, use the hook in your component. By using the hook, you have full control of when and how to upload files.

src/routes/index.tsx
import { useUploadThing } from "~/utils/uploadthing";
 
async function compress(file: File) {
  // Run some compression algorithm on the file
  return file;
}
 
export default function Home() {
  const { startUpload } = useUploadThing("profileImage", {
    onClientUploadComplete: () => {
      alert("Upload Completed");
    },
  });
 
  return (
    <main>
      <input
        type="file"
        onChange={async (e) => {
          const file = e.target.files?.[0];
          if (!file) return;
 
          // Do something with the file before uploading
          const compressed = await compress(file);
 
          // Then start the upload of the compressed file
          await startUpload([compressed]);
        }}
      />
    </main>
  );
}