DEV Community

Cover image for Build Your Own Image Search Engine with Unsplash API - Muhammad Kashif Pathan
Muhammad Kashif Pathan
Muhammad Kashif Pathan

Posted on • Edited on

Build Your Own Image Search Engine with Unsplash API - Muhammad Kashif Pathan

Introduction

Have you ever wanted to create your own image search engine? In this project, I built a simple yet powerful image search engine using HTML, CSS, JavaScript, and the Unsplash API. It allows users to search for high-quality images and load more results seamlessly. Let’s dive into the details!

Features

  • Search millions of images from Unsplash
  • Fast & responsive UI
  • Show More button to load extra results
  • Works on both desktop & mobile
  • Lightweight and easy to implement

Live Demo

You can check out the live version of this project here:
🔗 Live Demo

How I Built It

1. Setting Up the HTML Structure

First, I created a simple layout with an input field for search, a results section, and a Show More button.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Build a Stunning Image Search Engine Using Unsplash API in JavaScript! - Muhammad Kashif Pathan</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <main>
        <div class="container">
            <div class="hero">
                <h1>Find the Perfect Image for Your Project</h1>
                <p>Search millions of high-quality images powered by Unsplash. Just type your keyword and explore
                    stunning visuals in seconds!</p>
                <form>
                    <input type="text" placeholder="Search for beautiful images...">
                    <img src="search-icon.svg" alt="search-icon">
                </form>
                <p class="discover">Discover breathtaking images for free. Start searching now!</p>
            </div>

            <div class="search-result">
                <div id="loader" class="loader"></div>
            </div>
            <div class="show-more">
                <button class="show-more-btn">Show More</button>
            </div>
        </div>
    </main>
    <footer>
        <p>Powered by Unsplash API | Developed by Muhammad Kashif Pathan</p>
    </footer>
    <script src="script.js"></script>
</body>

</html>

Enter fullscreen mode Exit fullscreen mode

2. Adding Styling with CSS

For a clean and modern look, I styled the page using CSS. I also designed a dark theme-friendly scrollbar.

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
}

body {
    color: white;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    background-color: rgb(9, 9, 9);
}

main {
    width: 100%;
    height: calc(100vh - 52px);
    overflow: hidden auto;
    text-align: center;
    background-image: linear-gradient(rgba(0, 0, 0, 0.986), rgba(0, 0, 0, 0.936)),
        url("bg-img.jpeg");
    background-size: cover;
}

main::-webkit-scrollbar {
    width: 0;
}

.container .hero {
    background-image: linear-gradient(rgba(0, 0, 0, 0.968), rgba(0, 0, 0, 0.635)),
        url("coding.jpg");
    background-size: cover;

    padding: 20px;
    background-position: center;
    background-size: cover;
}

.hero h1 {
    margin-bottom: 20px;
    background-color: rgba(10, 198, 60, 0.1);
    border: 2px solid rgb(10, 198, 60);
    border-radius: 50px;
    padding: 10px 20px;
    font-size: 16px;
    display: inline-flex;
}

.hero p:nth-child(2) {
    margin: 20px auto;
}

.hero form {
    display: flex;
    border-radius: 5px;
    width: 100%;
    justify-content: center;
}

.hero form input {
    border: none;
    outline: none;
    border-radius: 10px 15px 15px 10px;
    padding: 10px;
    background-color: black;
    color: white;
    max-width: 500px;
    width: 100%;
    position: relative;
    left: 20px;
    font-size: 16px;
}

.hero form img {
    padding: 10px;
    cursor: pointer;
    border-radius: 50%;
    background-color: rgb(10, 198, 60);
    border: 5px solid rgb(24, 24, 24);
    position: relative;
    left: -20px;
    transition: all 0.8s linear;
}

.hero form img:hover {
    border: 5px solid rgb(10, 198, 60);
}

.hero .discover {
    margin: 20px auto;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 90%;
}

.show-more button {
    border: none;
    padding: 10px 20px;
    cursor: pointer;
    border-radius: 0 5px 5px 0;
    background-color: rgb(10, 198, 60);
    color: white;
    font-weight: 700;
    transition: background-color 0.3s ease;
}

.hero form button:hover,
.show-more button:hover {
    background-color: rgb(5, 158, 28);
}

.search-result {
    width: 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: center;
    margin-top: 20px;
    padding: 20px;
}

.search-result a {
    width: calc(33.333% - 20px);
    max-width: 300px;
    background-color: transparent;
    border-radius: 5px;
    overflow: hidden;
    transition: all 0.3s ease;
}

.search-result a img {
    width: 100%;
    height: 200px;
    object-fit: cover;
    object-position: center;
    display: block;
}

.search-result a:hover {
    transform: scale(1.05);
}


.hide {
    display: none;
}

::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-track {
    background: #1e1e1e;
    border-radius: 10px; 
}

::-webkit-scrollbar-thumb {
    background: #555;
    border-radius: 10px; 
    transition: all 0.3s ease;
}

::-webkit-scrollbar-thumb:hover {
    background: #888;
}

::-webkit-scrollbar-thumb:active {
    background: #aaa;
}

* {
    scrollbar-width: thin;
    scrollbar-color: #555 #1e1e1e;
}

.loader {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 50px;
    height: 50px;
    border: 6px solid rgba(255, 255, 255, 0.3);
    border-top-color: #fff;
    border-radius: 50%;
    animation: spin 1s linear infinite;
    z-index: 1000;
    background-color: transparent;
    display: none;
}

.loader {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 50px;
    height: 50px;
    border: 6px solid rgba(255, 255, 255, 0.3);
    border-top-color: #3498db;
    border-radius: 50%;
    animation: spin 1s linear infinite;
    display: none;
}

@keyframes spin {
    0% {
        transform: translate(-50%, -50%) rotate(0deg);
    }

    100% {
        transform: translate(-50%, -50%) rotate(360deg);
    }
}


.show-more {
    display: flex;
    justify-content: center;
}

.show-more-btn {
    margin-top: 20px;
    padding: 10px 20px;
    border-radius: 5px;
    font-size: 14px;
    font-weight: 700;
    display: none;
}

footer p {
    font-size: 10px;
    color: gray;
    font-weight: 700;
    padding: 20px;
    height: 52px;
}

@media (max-width: 768px) {
    .search-result a {
        width: calc(50% - 10px);
    }
}

@media (max-width: 550px) {
    main {
        padding: 20px 0;
    }

    .container .hero {
        padding: 20px 10px;
    }

    .hero, .hero h1 {
        font-size: 12px;
    }

    .search-result {
        padding: 10px;
    }

    .search-result a {
        min-width: 100%;
    }

    .search-result a img {
        height: 250px;
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Fetching Images from Unsplash API

The main functionality is powered by JavaScript and the Unsplash API. Here’s how I handled image fetching and display:

const accessKey = "YOUR_UNSPLASH_ACCESS_KEY";

const form = document.querySelector("form");
const searchInp = document.querySelector("input");
const searchResult = document.querySelector(".search-result");
const showMoreBtn = document.querySelector(".show-more-btn");
const loader = document.getElementById("loader");
const discoverText = document.querySelector(".discover");

let keyword = "";
let page = 1;

// ✅ Function to fetch images
async function searchImages() {
    keyword = searchInp.value.trim();
    if (!keyword) return;

    // Show loader and clear previous results for a new search
    if (page === 1) {
        searchResult.innerHTML = "";
        discoverText.textContent = "";
        discoverText.style.display = "block";
    }

    loader.style.display = "block";

    const url = `https://api.unsplash.com/search/photos?page=${page}&query=${keyword}&client_id=${accessKey}&per_page=12`;

    try {
        const response = await fetch(url);
        if (!response.ok) throw new Error("Failed to fetch images");

        const data = await response.json();
        const results = data.results;

        if (results.length === 0) {
            discoverText.textContent = `No images found for "${keyword}". Try a different keyword.`;
            showMoreBtn.style.display = "none";
            loader.style.display = "none";
            return;
        }

        // Append new images
        results.forEach((result) => {
            const image = document.createElement("img");
            image.src = result.urls.small;
            image.alt = result.alt_description || "Search result image";

            const imageLink = document.createElement("a");
            imageLink.href = result.links.html;
            imageLink.target = "_blank";

            imageLink.appendChild(image);
            searchResult.appendChild(imageLink);
        });

        discoverText.style.display = "none";

        showMoreBtn.style.display = results.length > 0 ? "block" : "none";
    } catch (error) {
        console.error("Error fetching images:", error);
        searchResult.innerHTML = `<p class="error-message">Something went wrong. Please try again later.</p>`;
    } finally {
        // Hide loader after fetching is complete
        setTimeout(() => {
            loader.style.display = "none";
        }, 500);
    }
}

form.addEventListener("submit", (e) => {
    e.preventDefault();
    page = 1;
    searchImages();
});

showMoreBtn.addEventListener("click", () => {
    page++;
    searchImages();
});

Enter fullscreen mode Exit fullscreen mode

Here’s how the project looks:

Final Project Screenshot

Final Project Screenshot With Search Result

🔥 Final Thoughts

This project is a great way to practice working with APIs, handling asynchronous JavaScript, and creating a user-friendly UI. If you want to expand it, you can:

  • Add pagination for infinite scrolling
  • Implement a light/dark mode switch
  • Allow users to download images directly

💡 What do you think?

I’d love to hear your feedback! If you liked this project, feel free to like, comment, and follow me for more awesome content. 🚀

🔗 GitHub Repository: GitHub

👨‍💻 Developed by Muhammad Kashif Pathan

🔥 Ready to build your own image search engine? Let’s discuss in the comments below!

Top comments (0)