Introduction
Let's be honest—handling files in web apps can be a pain. Whether it's your users' vacation selfies or important documents, you need somewhere reliable to store them. Firebase Storage has been one of the great solution. In this blog, we will explore Firebase Storage with React. Why you should consider Firebase Storage? Let's understand that:
- It's surprisingly easy to set up (even for someone who gets nervous around backend stuff)
- The upload progress tracking is a game-changer for user experience
- It plays nicely with React and TypeScript
Getting Started
First things first, head over to the Firebase Console. If you haven't already, create a project. Once inside, enable Storage under the Build section—it's just a few clicks.
Next, let's add Firebase to our project:
npm install firebase
Then create a firebase.ts
file with your configuration:
import { initializeApp } from "firebase/app";
import { getStorage } from "firebase/storage";
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_AUTH_DOMAIN",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_STORAGE_BUCKET.appspot.com",
messagingSenderId: "YOUR_SENDER_ID",
appId: "YOUR_APP_ID",
};
const app = initializeApp(firebaseConfig);
export const storage = getStorage(app);
Don't worry about memorizing this—you will always copy-paste it from the Firebase console anyway!
Single File Uploads
Let's create a component that lets users upload a single file. We'll also show them a progress bar so they're not left wondering if their file disappeared into the digital void.
Here's a component I built that handles a single file upload:
import React, { useState, ChangeEvent } from 'react';
import { storage } from '../firebase';
import { ref, uploadBytesResumable, getDownloadURL, deleteObject } from 'firebase/storage';
const FileUpload: React.FC = () => {
const [file, setFile] = useState<File | null>(null);
const [progress, setProgress] = useState<number>(0);
const [downloadURL, setDownloadURL] = useState<string>('');
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
setFile(e.target.files[0]);
}
};
const handleUpload = () => {
if (!file) return;
const storageRef = ref(storage, `uploads/${file.name}`);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on(
'state_changed',
(snapshot) => {
const percent = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
setProgress(Math.round(percent));
},
(error) => console.error('Upload failed', error),
async () => {
const url = await getDownloadURL(uploadTask.snapshot.ref);
setDownloadURL(url);
}
);
};
const handleDelete = async () => {
if (!file) return;
const storageRef = ref(storage, `uploads/${file.name}`);
try {
await deleteObject(storageRef);
setDownloadURL('');
setProgress(0);
alert('File deleted successfully!');
} catch (error) {
console.error('Delete failed', error);
}
};
return (
<div>
<h2>File Upload</h2>
<input type="file" onChange={handleFileChange} />
<button onClick={handleUpload}>Upload</button>
{progress > 0 && <p>Progress: {progress}%</p>}
{downloadURL && (
<div>
<p>File URL: <a href={downloadURL} target="_blank" rel="noreferrer">{downloadURL}</a></p>
<button onClick={handleDelete}>Delete File</button>
</div>
)}
</div>
);
};
export default FileUpload;
Multiple File Uploads
In real-world apps, users often need to upload multiple files at once. Maybe it's a bunch of photos from their weekend trip or several documents for an application. Here's how I handled that:
import React, { useState, ChangeEvent } from 'react';
import { storage } from '../firebase';
import { ref, uploadBytesResumable, getDownloadURL } from 'firebase/storage';
interface UploadedFile {
name: string;
url: string;
}
const MultiFileUpload: React.FC = () => {
const [files, setFiles] = useState<File[]>([]);
const [uploadProgress, setUploadProgress] = useState<Record<string, number>>({});
const [downloadLinks, setDownloadLinks] = useState<UploadedFile[]>([]);
const handleFilesChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.files) {
setFiles(Array.from(e.target.files));
}
};
const handleUpload = () => {
files.forEach((file) => {
const storageRef = ref(storage, `uploads/${file.name}`);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on(
'state_changed',
(snapshot) => {
const percent = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
setUploadProgress((prev) => ({ ...prev, [file.name]: Math.round(percent) }));
},
(error) => console.error('Upload failed', error),
async () => {
const url = await getDownloadURL(uploadTask.snapshot.ref);
setDownloadLinks((prev) => [...prev, { name: file.name, url }]);
}
);
});
};
return (
<div>
<h2>File Upload</h2>
<input type="file" multiple onChange={handleFilesChange} />
<button onClick={handleUpload}>Upload All</button>
{files.map((file) => (
<div key={file.name}>
<p>{file.name}: {uploadProgress[file.name] || 0}%</p>
</div>
))}
{downloadLinks.map((file) => (
<div key={file.name}>
<p>Uploaded: <a href={file.url} target="_blank" rel="noreferrer">{file.name}</a></p>
</div>
))}
</div>
);
};
export default MultiFileUpload;
Putting It All Together
Now let's combine these components in our main App:
import React from 'react';
import FileUpload from './components/FileUpload';
import MultiFileUpload from './components/MultiFileUpload';
const App: React.FC = () => {
return (
<div className="App">
<FileUpload />
<hr />
<MultiFileUpload />
</div>
);
};
export default App;
Keeping Things Secure: Firebase Rules
Here's something I learned the hard way: Don't forget to set up security rules! Otherwise, anyone could potentially access or modify your files.
Here's a basic rule I use that allows only authenticated users to upload and download files:
service firebase.storage {
match /b/{bucket}/o {
match /uploads/{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
You can configure this in Firebase Console → Storage → Rules.
Conclusion
This blog covers the first step to start your journey with Firebase storage. If you want to go a step ahead, you can implement the following:
- Custom file validation for specific project needs
- Better integration with Firebase Authentication for user-specific storage
- Auto-generating thumbnails for image uploads
- Implementing drag-and-drop uploads for a nicer UX If you like this blog and want to learn more about Frontend Development and Software Engineering, you can follow me on Dev.to.
Top comments (0)