Upload Image with Next App Router and Firebase
3 min readMay 31, 2024
This post will guide you to upload file with firebase and show all images from storage firebase.
Prerequisite:
- Next App Router
- Firebase
- Heroicon
Installation
Run this command on your terminal to install the dependencies
// next latest version
npx create-next-app@latest
npm install firebase
npm i @heroicons/react
Step 1 — Create UI
Create new page on /app/upload/page.tsx
with client component and follow this code below. on this page you can select image and preview the image before submit.
"use client"
import { PlusIcon, TrashIcon } from "@heroicons/react/20/solid";
import Image from "next/image";
import { ChangeEvent, useRef, useState } from "react";
export default function Page() {
const [image, setImage] = useState<File>();
const buttonRef = useRef<HTMLInputElement>(null);
function handleChange(e: ChangeEvent<HTMLInputElement>) {
const image = e?.target?.files?.[0];
setImage(image)
}
function deleteImage() {
setImage(undefined);
}
return (
<main className="mx-auto mt-4 max-w-5xl">
<div className="flex flex-col gap-4">
<p className="text-2xl font-bold">Upload Image</p>
<div className="flex gap-2">
{image ?
<div className="flex flex-col gap-2">
<div className="relative">
<Image
alt="image preview"
width={280}
height={200}
className="rounded-xl w-[200px] h-[200px] object-cover"
src={URL.createObjectURL(image)}
/>
<button className="absolute top-2 right-2 bg-red-200/10 p-2 rounded-lg text-red-400"
onClick={deleteImage}>
<TrashIcon className="w-6 h-6" />
</button>
</div>
<button className="bg-white text-black py-2 rounded-lg font-bold">
Upload
</button>
</div>
: (
<>
<label htmlFor="upload-image">
<button type="button"
className="border-dashed border-2 border-slate-400 p-20 text-slate-400 rounded-2xl"
onClick={() => { buttonRef.current?.click() }}>
<PlusIcon className="w-10 h-10" />
</button>
</label>
<input id="upload-image"
type="file"
accept="image/png, image/gif, image/jpeg"
className="hidden"
ref={buttonRef}
onChange={handleChange}
/>
</>
)}
</div>
<div>
<p className="text-2xl font-bold">Uploaded Images:</p>
<hr className="border-slate-600 mt-2" />
</div>
</div>
</main>
)
}
Step 2 — Integrate Firebase
After done with UI now we can integrate UI with Firebase. Follow this step below
- Add Firebase Config and init the firebase client.
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_AUTH_DOMAIN",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_STORAGE_BUCKET",
messagingSenderId: "YOUR_MESSAGE_SENDER_ID",
appId: "YOUR_APP_ID",
measurementId: "YOUR_MEASUREMENT_ID"
};
const app = initializeApp(firebaseConfig);
export default function Page() { ... }
2. Get the storage
firebase
export default function Page() {
const storage = getStorage(app);
...
}
3. Add handling for submit image to firebase
export default function Page() {
...
async function uploadImage() {
if (!image) {
alert("Image not found!");
return;
}
const imageName = image.name.split(".");
const storageRef = ref(storage, imageName[0]);
uploadBytes(storageRef, image).then(() => {
alert("Uploaded image!");
setImage(undefined);
}).catch(() => {
alert("Failed to upload the image!")
});
}
return (
<main className="...">
<div className="...">
...
<div className="...">
{image ?
<div className="...">
...
<button className="bg-white text-black py-2 rounded-lg font-bold"
onClick={uploadImage}>
Upload
</button>
</div>
: (
...
)}
</div>
...
</div>
</main>
)
}
4. Modify config next.config.js
like this
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [{
hostname: "firebasestorage.googleapis.com"
}]
}
}
module.exports = nextConfig
5. Get All images from firebase
export default function Page() {
const [urls, setUrls] = useState<string[]>([]);
...
useEffect(() => {
async function getAllImages() {
const listRef = ref(storage, "/");
const urlTemp: string[] = [];
await listAll(listRef)
.then((res) => {
const promiseUrls = res.items.map(async (itemRef) => {
const url = await getDownloadURL(itemRef);
return url;
});
Promise.all(promiseUrls).then((urls) => {
setUrls(urls)
});
}).catch((error) => {
// Uh-oh, an error occurred!
});
}
getAllImages();
}, [])
<main className="mx-auto mt-4 max-w-5xl">
<div className="flex flex-col gap-4">
...
<div>
...
<div className="flex flex-wrap gap-4 mt-2">
{urls.map((url, index) =>
<Image key={`img-${index}`} src={url} width={200} height={200} className="w-[200px] h-[200px] rounded-lg object-cover" alt="image"/>
)}
</div>
</div>
</div>
</main>
}
Thank You for Read.