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)