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.
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,
});
}
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"
/>
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());
});
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.
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
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)