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. 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
import { createUploadthing } from "uploadthing/server";
import type { FileRouter } from "uploadthing/server";
const f = createUploadthing();
export const uploadRouter = {
profileImage: f({
image: {
maxFileSize: "4MB",
},
})
.middleware(({ req }) => {
// Check some condition based on the incoming requrest
if (!req.headers.get("x-some-header")) {
throw new Error("x-some-header is required");
}
// Return some metadata to be stored with the file
return { foo: "bar" as const };
})
.onUploadComplete(({ file, metadata }) => {
metadata;
// ^?
console.log("upload completed", file);
}),
} 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 { createServerHandler } from "uploadthing/server";
import { uploadRouter } from "~/server/uploadthing";
export const { GET, POST } = createServerHandler({
router: uploadRouter,
});
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 { generateComponents } 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, UploadDropzone, Uploader } =
generateComponents<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: 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
:
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.
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>
);
}