File Uploading is one of the crucial parts of any app, In this Blog I will tell you How you can upload images on the NextJS application.
For the Past Few Days, I have been working on a project where I need to apply this functionality so I tried many things But UploadThing works as expected for the NextJS project.
What you will learn after this Blog -
How to Initialize UploadThing on a Project?
How to store this IMG URL to the MongoDB?
How to fetch this IMG from the Database(MongoDB)
Firstly Let's Create a NextJS application, you can create this via this -
npx create-next-app@latest
Now, go to UploadThing and SignUp, create a new app, and store these to .env.local
UPLOADTHING_SECRET=Your Secret key
UPLOADTHING_APP_ID=Your Id
after that install uplaodthing
through this command
npm install uploadthing @uploadthing/react
Now inside the app directory create a folder api
and inside that create another folder named uploadthing
and then create a file named cors.ts
and put these things into it-
import { createUploadthing, type FileRouter } from "uploadthing/next";
const f = createUploadthing();
export const ourFileRouter = {
imageUploader: f({ image: { maxFileSize: "4MB" } }).onUploadComplete(
async ({ file }) => {
console.log("file url", file.url);
}
),
} satisfies FileRouter;
export type OurFileRouter = typeof ourFileRouter;
inside uploadthing
folder create route.ts
and put these things into this -
import { createNextRouteHandler } from "uploadthing/next";
import { ourFileRouter } from "../uploadthing/cors";
export const { GET, POST } = createNextRouteHandler({
router: ourFileRouter,
});
You can add UploadThing styling, for that you should need to add this to your app/layout.tsx
import "@uploadthing/react/styles.css";
Now Create the UploadThing Components, it will be good if you create a utils
folder and create a file name uploadthing.ts
import { generateComponents } from "@uploadthing/react";
import type { OurFileRouter } from "~/app/api/uploadthing/core";
export const { UploadButton, UploadDropzone, Uploader } =
generateComponents<OurFileRouter>();
Now Before using this button Let's set MongoDB so that our Link will go to the database
Firstly create the ConnectToDB function inside the app and create one folder named db
and inside that create db.js
file with this -
import mongoose from "mongoose";
const connectToDB = async () => {
try {
await mongoose.connect(process.env.MONGO_URL!);
console.log("MongoDB connected");
} catch (err: any) {
console.log(err.message);
process.exit(1);
}
};
export default connectToDB;
Now Let's create a model for the database -
const mongoose = require("mongoose");
const notesSchema = new mongoose.Schema({
notesTitle: {
type: String,
required: true,
},
notesDescription: {
type: String,
required: true,
},
fileLink: {
type: String,
required: true,
},
creator: {
type: String,
},
creatorImgUrl: {
type: String,
},
});
const Notes = mongoose.models.Notes || mongoose.model("Notes", notesSchema);
export default Notes;
Now let's create a api
route for handling this inside api
folder create a folder name upload
import connectToDB from "@/db/db";
import Notes from "@/models/Notes";
import { NextRequest, NextResponse } from "next/server";
connectToDB();
export async function POST(req: NextRequest, res: NextResponse) {
try {
const reqBody = await req.json();
const { notesTitle, notesDescription, fileLink, creator, creatorImgUrl } =
reqBody;
const Note = new Notes({
notesTitle,
notesDescription,
fileLink,
creator,
creatorImgUrl,
});
const NewNotes = await Note.save();
console.log(NewNotes);
const response = NextResponse.json({
message: "Notes created successfully",
success: true,
NewNotes,
});
return response;
} catch (error: any) {
const response = NextResponse.json(
{ error: error.message },
{ status: 500 }
);
return response;
}
}
export async function GET() {
try {
const Note = await Notes.find();
const response = NextResponse.json({
message: "Review fetched successfully",
success: true,
Note,
});
return response;
} catch (error: any) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}
Now Let's handle this route for the front and create one folder Upload
inside app
"use client";
import React, { useState } from "react";
import { UploadButton } from "../../utils/uploadthing";
import { useRouter } from "next/navigation";
import { useClerk } from "@clerk/clerk-react";
import LoadingSpinner from "@/components/LoadingSpinner";
const Page: React.FC = () => {
const [notesTitle, setNotesTitle] = useState<string>("");
const [notesDescription, setNotesDescription] = useState<string>("");
const [fileLink, setFileLink] = useState<string>();
const [loading, setLoading] = useState(false);
const { user } = useClerk();
const router = useRouter();
const handleNotesTitleChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
setNotesTitle(event.target.value);
};
const handleNotesDescriptionChange = (
event: React.ChangeEvent<HTMLTextAreaElement>
) => {
setNotesDescription(event.target.value);
};
const handleSubmit = async () => {
try {
if (!notesTitle || !notesDescription || !fileLink) {
alert("Please provide both Content and fileLink before submitting.");
return;
}
setLoading(true);
const response = await fetch("/api/upload", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
notesTitle,
fileLink,
notesDescription,
creator: user?.fullName,
creatorImgUrl: user?.imageUrl,
}),
});
if (response.ok) {
router.push("/dashboard");
} else {
alert(`Submission failed. Status: ${response.status}`);
}
} catch (error: any) {
alert(`Error during submission: ${error.message}`);
} finally {
setLoading(false);
}
};
return (
<div className="flex justify-center flex-col items-center h-screen">
<h1 className="text-transparent bg-gradient-to-br from-red-800 to-blue-600 bg-clip-text text-3xl sm:text-5xl font-bold mb-4">
Upload Notes
</h1>
<div className="bg-[radial-gradient(ellipse_at_bottom,_var(--tw-gradient-stops))] from-gray-400 via-gray-600 to-gray-800 p-6 rounded-md shadow-md mb-6 sm:w-96">
<form>
<div className="mb-4">
<label htmlFor="notesTitle" className="block text-gray-300">
Notes Title
</label>
<input
type="text"
id="notesTitle"
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-indigo-500"
placeholder="Enter notes title"
value={notesTitle}
onChange={handleNotesTitleChange}
/>
</div>
<div className="mb-4">
<label htmlFor="notesDescription" className="block text-gray-300">
Notes Description
</label>
<textarea
id="notesDescription"
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-indigo-500"
placeholder="Enter notes description"
value={notesDescription}
onChange={handleNotesDescriptionChange}
/>
</div>
<div>
<UploadButton
endpoint="imageUploader"
onClientUploadComplete={(res: any) => {
console.log("Url: ", res[0].url);
setFileLink(res[0].url);
console.log(res);
alert("Upload Completed");
}}
onUploadError={(error: Error) => {
alert(`ERROR! ${error.message}`);
}}
/>
</div>
{loading ? (
<LoadingSpinner />
) : (
<button
type="button"
className="w-full py-2 bg-indigo-500 text-white rounded-md hover:bg-indigo-600 focus:outline-none mt-8"
onClick={handleSubmit}
>
Submit
</button>
)}
</form>
</div>
</div>
);
};
export default Page;
Here in the image, you see you have the Choose File button which is coming through UploadThing and when you choose a file it will be saved to your uploadthing
dashboard and I am saving this URL to the MongoDB database.
Here is how you can upload images on the UploadThing and later store them to mongoDB, Hope this Blog Helped you to learn something new.
Happy Coding🎉