Next.js App Router Setup
Oh, a little bleeding edge are we? We're big fans of app/
and server
components, and we think you'll love what we've built 🙏
Setting up your environment
Install the packages
npm install uploadthing @uploadthing/react
Add env variables
UPLOADTHING_SECRET=... # A secret key for your app (starts with sk_live_)
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)
Set Up A FileRouter
Creating your first FileRoute
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, type FileRouter } from "uploadthing/next";
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);
// !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback
return { uploadedBy: metadata.userId };
}),
} satisfies FileRouter;
export type OurFileRouter = typeof ourFileRouter;
Create a Next.js 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 { createNextRouteHandler } from "uploadthing/next";
import { ourFileRouter } from "./core";
// Export routes for Next App Router
export const { GET, POST } = createNextRouteHandler({
router: 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}"],
...
});
Create The UploadThing Components (Recommended)
We provide components to make uploading easier. We highly recommend
re-exporting them with the types assigned, but you CAN import the components
individually from @uploadthing/react
instead.
import { generateComponents } from "@uploadthing/react";
import type { OurFileRouter } from "~/app/api/uploadthing/core";
export const { UploadButton, UploadDropzone, Uploader } =
generateComponents<OurFileRouter>();
Mount A Button And Upload!
"use client";
import { UploadButton } from "~/utils/uploadthing";
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<UploadButton
endpoint="imageUploader"
onClientUploadComplete={(res) => {
// Do something with the response
console.log("Files: ", res);
alert("Upload Completed");
}}
onUploadError={(error: Error) => {
// Do something with the error.
alert(`ERROR! ${error.message}`);
}}
/>
</main>
);
}
(Optional) UploadThing SSR Plugin
UploadThing needs to get info from your server to get permissions info. Normally this means a loading state. We built an optional plugin to prevent that
To add SSR hydration and avoid that loading state, simply render the
<NextSSRPlugin />
hydration helper in the body of your root layout before
the children.
import { NextSSRPlugin } from "@uploadthing/react/next-ssr-plugin";
import { extractRouterConfig } from "uploadthing/server";
import { ourFileRouter } from "~/app/api/uploadthing/core";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<NextSSRPlugin
/**
* The `extractRouterConfig` will extract **only** the route configs
* from the router to prevent additional information from being
* leaked to the client. The data passed to the client is the same
* as if you were to fetch `/api/uploadthing` directly.
*/
routerConfig={extractRouterConfig(ourFileRouter)}
/>
{children}
</body>
</html>
);
}
🎉 You're Done!
Want to customize the components? Check out the "Theming" page
Want to make your own components? Check out our useUploadThing hook