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.
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
.
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
.
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.
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.
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.
// 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: createUploadThing
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 helper
First, generate a typed helper using the generateSolidHelpers
function from
@uploadthing/solid
:
import { generateSolidHelpers } from "@uploadthing/solid";
import type { OurFileRouter } from "~/server/uploadthing";
export const { createUploadThing } = generateSolidHelpers<OurFileRouter>();
Use the generated helper
Then, use the createUploadThing
function in your component. By using the raw
createUploadThing
function, you have full control of when and how to upload
files.
import { createUploadThing } from "~/utils/uploadthing";
async function compress(file: File) {
// Run some compression algorithm on the file
return file;
}
export default function Home() {
const { startUpload } = createUploadThing("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>
);
}