DEV Community

Bunmi Oye
Bunmi Oye

Posted on

How I Built an Infinite Scroll Photo Gallery with Vue 3 and the Unsplash API

Creating a photo gallery powered by the Unsplash API is a great way to build a media-rich, user-driven frontend experience. This walkthrough covers the core components of a Vue 3 + TypeScript application that integrates with Unsplash’s search endpoint to deliver an infinite-scroll image gallery with search support.

gallery

Setting Up the Project: Vite, Vue 3, TypeScript

The project was scaffolded using Vite, Vue 3, and TypeScript to ensure fast dev builds, modern syntax, and type safety. The base structure included a main.ts entry file, a minimal App.vue shell, and clean CSS defaults via a custom style.css.

To streamline development, Volar was installed via the VS Code extension store for Vue-specific TypeScript support.

💡 Tip: If you're using TypeScript with Vue, Volar offers the best IDE experience in VS Code right now.

Building the Photo Fetch Logic

I created a dedicated composable (hook) to fetch photos from Unsplash’s search/photos endpoint using axios. It accepted a query string and page number as parameters.

This modular fetch logic allowed:

  • Dynamic searching
  • Infinite scrolling
  • Reusability across components
import { createApi } from 'unsplash-js';
import { API_KEY, PHOTO_COUNT_PER_PAGE } from '../constants';

export async function fetchPhotos(query: string, page: number = 1) {
  const api = createApi({
    accessKey: API_KEY,
  });

  return await api.search.getPhotos({
    query,
    orientation: 'landscape',
    page,
    perPage: PHOTO_COUNT_PER_PAGE,
  });
}
Enter fullscreen mode Exit fullscreen mode

Using a composable pattern also made it easy to track request states like loading, success, or error, making the UI much easier to manage.

Laying Out the Image Grid

Images from the API were rendered in a grid layout via a dedicated component that accepted the photo data as props. I used CSS Grid to create a responsive, flexible layout that adapts to different screen sizes.

<Gallery
  :photos="photos"
  :status="fetchStatus"
  :is-loading-initial="isLoadingInitial"
  :is-loading-more="isLoadingMore"
/>
Enter fullscreen mode Exit fullscreen mode

Each image included proper alt text (based on Unsplash data) and CSS rules to maintain consistent aspect ratios and spacing.

Making It Searchable

A search input was added at the top of the page, allowing users to dynamically query the Unsplash API.

Here’s how it worked:

  • When a new term was submitted, the photo list was cleared
  • The query state updated
  • A new request was made, starting from page 1

This enabled users to explore terms like “mountains”, “sunsets”, or “cityscapes” in real-time through Unsplash’s library.

Adding Infinite Scroll Without Breaking Everything

To reduce friction and improve the experience, I implemented infinite scroll. As users approached the bottom of the page, the app automatically requested the next page of results and appended them to the current list.

Scroll position was tracked with an event listener, and throttling was applied to prevent spammy API requests.

onMounted(() => {
  const observer = new IntersectionObserver(loadMorePhotos);
  observer.observe(loadMoreRef.value);

  onBeforeUnmount(() => observer.disconnect());
});
Enter fullscreen mode Exit fullscreen mode

This created a smooth, uninterrupted browsing experience, essential for image-heavy interfaces.

⚠️ Note: Be mindful of Unsplash’s API rate limits if you’re implementing this yourself.

What Happens When Things Break

I added visual indicators to show when content was loading and included fallback UI for error states, like when the API request failed or hit its limit.

gallery with loading photos

Whether it was a network hiccup or exceeding Unsplash’s rate limit, the app informed users without breaking flow or crashing the UI.

What I Learned (and You Might, Too)

This project combined Vue 3’s modern frontend tools with Unsplash’s API to deliver a fast, engaging, and responsive image gallery.

Key takeaways:

  • Modular composables make fetch logic reusable and testable
  • Infinite scroll feels great but needs careful handling
  • Semantic HTML and thoughtful styling go a long way in user experience

If you're looking to build something similar, start simple. Then iterate.

What’s Next?

Some upgrades I would explore:

  • Debounced search input to reduce redundant API requests
  • Lazy-loading images to improve performance
  • A photo modal for fullscreen previews and metadata
  • LocalStorage (or backend) support for saving favorites
  • Dark mode toggle (because why not?)

Final Thoughts

This project was a fun way to learn and apply Vue 3 in a hands-on setting, while integrating with a real-world API. If you’re exploring Vue or just want to build something visual and interactive, a photo gallery like this is a great place to start.

Working with Vue was a new experience for me, and I talked about that experience here: I Thought React Was Clean, Then I Tried Vue


Tooling I Used

  • Vite – for super fast development
  • 🟦 TypeScript – for static typing
  • 🌄 Unsplash API – for fetching beautiful images
  • 🧱 Vue 3 – using the Composition API
  • 🖼️ SCSS – scoped in .vue files for styling

🔗 View the code here


Thanks for reading! I'd love to hear your thoughts and experiences with Vue. What stood out to you the most? Let me know in the comments.

Happy coding. 🚀

Top comments (0)