Building a Stunning Gallery App with React, Firebase, Unsplash API, and Tailwind CSS
Welcome to this step-by-step guide on building a beautiful gallery app using popular technologies like React, Firebase, the Unsplash API, and Tailwind CSS. In this tutorial, we'll walk you through the entire process, from project setup to the creation of key components. By the end, you'll have a working gallery app that you can use as a foundation for your own projects.
Technologies Used
Before we dive into the code, let's briefly introduce the technologies we'll be using:
React: A popular JavaScript library for building user interfaces.
Firebase: A comprehensive platform for building web and mobile applications with features like authentication.
Unsplash API: A free and reliable source of high-quality images for developers.
Tailwind CSS: A utility-first CSS framework for rapid UI development.
Project Setup
To get started, we'll set up our project using Create React App and Firebase. Make sure you have Node.js installed, then run the following commands:
npx create-react-app gallery-app
cd gallery-app
npm install firebase
Once you've created your Firebase project, replace the Firebase configuration in your firebase.js
file with your own project's configuration.
Now, let's explore the core components of our gallery app: LoginPage
and GalleryPage
.
LoginPage
Component
Explanation
The LoginPage
component handles user authentication. It uses Firebase's signInWithEmailAndPassword
function to authenticate users with their email and password. If authentication fails, it displays an error message.
import React, { useState } from 'react';
import { auth } from "../firebase";
import { signInWithEmailAndPassword } from "firebase/auth";
import { Link, useNavigate } from "react-router-dom";
const LoginPage = () => {
const navigate = useNavigate();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [notice, setNotice] = useState("");
const loginWithUsernameAndPassword = async (e) => {
e.preventDefault();
try {
await signInWithEmailAndPassword(auth, email, password);
navigate("./gallery");
} catch {
setNotice("You entered the wrong username or password.");
}
}
return (
// JSX for the login form
// ...
);
};
Code Explanation
In this component:
We import the necessary functions and components.
We use React hooks to manage the state of
email
,password
, andnotice
.The
loginWithUsernameAndPassword
function is called when the "Sign in" button is clicked. It attempts to sign in the user with the provided email and password using Firebase'ssignInWithEmailAndPassword
function.If authentication fails, an error message is displayed.
GalleryPage
Component
Explanation
The GalleryPage
component displays a gallery of images fetched from the Unsplash API. It also includes a search bar for filtering images by keywords.
import React, { useState, useEffect } from 'react';
import { auth } from "../firebase";
import { onAuthStateChanged } from "firebase/auth";
import { Link, useNavigate } from "react-router-dom";
const GalleryPage = () => {
const navigate = useNavigate();
const [searchResults, setSearchResults] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
// Check if the user is authenticated
const unsubscribe = onAuthStateChanged(auth, (user) => {
if (!user) {
// User is not authenticated, redirect to the login page
navigate('/');
}
});
// Clean up the listener when the component unmounts
return () => unsubscribe();
}, [navigate]);
const handleSearch = (query) => {
setError(null);
setLoading(true);
// Perform the API request here and update searchResults
fetchImagesFromUnsplash(query);
};
async function fetchImagesFromUnsplash(query) {
try {
const accessKey = "ASce65Gi4cay9Tk_RglFvqOlflyKG8MX9rc3erBpCRc";
// Make the API request and set searchResults with the fetched images
const response = await fetch(
`https://api.unsplash.com/search/photos?query=${query}&per_page=16`,
{
headers: {
Authorization: `Client-ID ${accessKey}`,
},
}
);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
setSearchResults(data.results);
setLoading(false);
} catch (error) {
setError('An error occurred while fetching images from Unsplash. Please try again later.');
setLoading(false);
}
}
return (
// JSX for the GalleryPage component
// ...
);
};
Code Explanation
In this component:
We use React hooks to manage the state of
searchResults
,loading
,error
, and to check if the user is authenticated.The
handleSearch
function is called when a search query is submitted. It initiates an API request to Unsplash to fetch images based on the query.The
fetchImagesFromUnsplash
function sends a request to the Unsplash API using the provided query and updates thesearchResults
state with the fetched images.
App.js
Configuration
Explanation
In App.js
, we configure routing using React Router, allowing us to switch between the LoginPage
and GalleryPage
components.
import React from 'react';
import { BrowserRouter, Routes, Route } from "react-router-dom";
import LoginPage from "./pages/LoginPage";
import GalleryPage from "./pages/GalleryPage";
import Layout from "./Layout";
const App = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<LoginPage />} />
<Route path="/gallery" element={<GalleryPage />} />
</Route>
</Routes>
</BrowserRouter>
);
};
Code Explanation
In this configuration:
We use the
BrowserRouter
component to enable client-side routing.We define routes for the root path, which renders the
Layout
component, and nested routes for theLoginPage
andGalleryPage
components.
Firebase Configuration
Explanation
The firebase.js
file initializes Firebase and provides access to authentication services using the Firebase configuration details from your Firebase project.
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
const firebaseConfig = {
// Your Firebase configuration details here
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
export { auth };
Code Explanation
In this configuration:
We import the necessary functions from Firebase.
We initialize Firebase using the provided
firebaseConfig
.We create an authentication object (
auth
) that provides access to Firebase authentication services.
Gallery
Component
Explanation
The Gallery
component is responsible for displaying images and allowing users to reorder them via drag-and-drop. It also handles fetching images from the Unsplash API.
import React, { useEffect, useState } from 'react';
const Gallery = ({ searchResults }) => {
const [images, setImages] = useState([]);
const [draggedImage, setDraggedImage] = useState(null);
useEffect(() => {
const apiUrl = 'https://api.unsplash.com/photos';
const accessKey = "##########_#############################"; // Replace with your actual access key
const perPage = 16;
if (!searchResults || searchResults.length === 0) {
// Fetch the first 16 pictures from the API by default
fetch(`${apiUrl}?per_page=${perPage}`, {
headers: {
Authorization: `Client-ID ${accessKey}`,
},
})
.then((response) => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then((data) => {
const imageUrls = data.map((photo) => photo.urls.regular);
setImages(imageUrls);
})
.catch((error) => {
console.error('Error fetching images from Unsplash:', error);
});
} else {
// If there are search results, update the images with the search results
const searchImageUrls = searchResults.map((photo) => photo.urls.regular);
setImages(searchImageUrls);
}
}, [searchResults]);
// ...
// Rest of the Gallery component code for drag-and-drop functionality
return (
// JSX for the Gallery component
// ...
);
};
Code Explanation
In this component:
We use React hooks to manage the state of
images
anddraggedImage
.In the
useEffect
hook, we fetch images from the Unsplash API using the provided access key. If there are no search results, it fetches the first 16 pictures from the API.The
handleDragStart
,handleDragEnd
,handleDragOver
, andhandleDrop
functions enable drag-and-drop functionality for reordering images.The component maps and renders the fetched images, allowing users to interact with them.
Conclusion
Congratulations! You've successfully built a stunning gallery app with React, Firebase, the Unsplash API, and Tailwind CSS. We've covered key components, authentication, image fetching, and more. Feel free to explore and customize this project further for your needs.
Additional Resources
That concludes our tutorial on building a gallery app with React, Firebase, the Unsplash API, and Tailwind CSS. We hope you found this guide helpful in starting your own projects. Happy coding!
Hosted Project
You can explore the hosted version of this project here.
Repository Link
The complete source code for this project can be found on GitHub.
Note: The default login credentials to access the gallery on the hosted link are as follows:
Username: user@example.com
Password: 1Password
We hope you enjoy building and customizing your gallery app. If you have any feedback or questions, please don't hesitate to reach out. Happy coding!
Subscribe to my newsletter
Read articles from Agbeniga Agboola directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by