Upload Image with Next App Router and Firebase

Nemesis Blog
3 min readMay 31, 2024

--

This post will guide you to upload file with firebase and show all images from storage firebase.

Prerequisite:

  1. Next App Router
  2. Firebase
  3. 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

  1. 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.

--

--