In this article, we will explore how to create an interactive image gallery inspired by Apple's iPhone Photos app, using Next.js, React, TypeScript, and Framer Motion. This approach not only provides a smooth and engaging user experience but also leverages users' familiarity with Apple's interface to offer an intuitive image browsing experience. Additionally, to see more interactive visual components like this one, you can visit smoothui.dev.
##Key Features
- Responsive grid design similar to the iOS Photos app
- Image selection functionality
- Deletion of selected images
- Gallery reset to its initial state
- Smooth animations using Framer Motion
##Code Breakdown
Let's analyze the main components of our ImageSelector:
###Imports and Initial Setup
"use client"
import { useCallback, useState } from "react"
import Image from "next/image"
import { AnimatePresence, motion } from "framer-motion"
import { Share2, Trash2 } from "lucide-react"
// Image imports and initial data configuration
We start by importing the necessary dependencies and setting up our initial image data.
###State Management
const [images, setImages] = useState<number[]>(
initialImages.map((img) => img.id)
)
const [selectedImages, setSelectedImages] = useState<number[]>([])
const [isSelecting, setIsSelecting] = useState(false)
We use React's useState hook to manage our component's state, including the list of images, selected images, and selection mode.
- images: IDs of the images currently in the gallery.
- selectedImages: IDs of the selected images.
- isSelecting: Boolean indicating whether we are in selection mode.
###Event Handlers
const handleImageClick = useCallback(
(id: number) => {
if (!isSelecting) return
setSelectedImages((prev) =>
prev.includes(id) ? prev.filter((imgId) => imgId !== id) : [...prev, id]
)
},
[isSelecting]
)
const handleDelete = useCallback(() => {
setImages((prev) => prev.filter((id) => !selectedImages.includes(id)))
setSelectedImages([])
}, [selectedImages])
const handleReset = useCallback(() => {
setImages(initialImages.map((img) => img.id))
setSelectedImages([])
setIsSelecting(false)
}, [])
const toggleSelecting = useCallback(() => {
setIsSelecting((prev) => !prev)
if (isSelecting) setSelectedImages([])
}, [isSelecting])
These event handlers manage the main logic of our application:
handleImageClick: Selects or deselects an image.handleDelete: Deletes the selected images.handleReset: Resets the gallery to its initial state.toggleSelecting: Toggles selection mode on or off.
We use useCallback to memoize these functions and avoid unnecessary re-renders.
###Gallery Rendering
return (
<div className="flex h-full w-full flex-col justify-between p-4">
{/ Header with Reset and Select buttons /}
{/ Image grid /}
{/ Selection toolbar /}
</div>
)
The main render function returns a container with a header, an image grid, and a selection toolbar.
###Image Grid
<motion.div className="grid grid-cols-3 gap-1 overflow-scroll" layout>
<AnimatePresence>
{images.map((id) => {
const image = imageMap.get(id)
if (!image) return null
return (
<motion.div
key={image.id}
layout
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{ type: "spring", stiffness: 300, damping: 25 }}
className="relative aspect-square cursor-pointer"
onClick={() => handleImageClick(image.id)}
>
{/ Image component /}
{/ Selection indicator /}
</motion.div>
)
})}
</AnimatePresence>
</motion.div>
The image grid uses Framer Motion for smooth animations when adding or removing images, similar to Apple's Photos app.
- We use Framer Motion's
motion.divto animate the entire grid. AnimatePresenceis used to animate the entry and exit of list elements.- Each image is wrapped in a
motion.divwith entry, exit, and layout animations. - The animations use a "spring" transition type for a more natural effect.
###Selection Toolbar
<AnimatePresence>
{isSelecting && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
className="bg-light1/20 dark:bg-dark1/20 absolute bottom-0 left-0 right-0 z-10 flex items-center justify-between p-4 bg-blend-luminosity backdrop-blur-xl"
>
{/ Share and Delete buttons /}
{/ Selected count /}
</motion.div>
)}
</AnimatePresence>
The selection toolbar appears in selection mode, allowing users to share or delete selected images, just like in the iOS Photos app.
##Conclusion
This tutorial has guided you through the process of creating an interactive image gallery inspired by the iOS Photos app. We have covered:
- Project setup and state management with React Hooks.
- Implementation of a responsive and attractive user interface.
- Using Framer Motion for smooth and natural animations.
- Event handling for image selection, deletion, and reset.
This component demonstrates how to combine Next.js, React, TypeScript, and Framer Motion to create modern and attractive user interfaces. Animations not only enhance the aesthetics but also provide visual feedback for user actions, improving the overall experience. To further improve this component, you could consider adding zoom functionality for images, implementing touch gestures for mobile devices, or integrating with a backend to dynamically load and save images. Remember, practice is key to mastering these technologies. Experiment with the code, tweak the animations, and make this component your own!
##See More
Visit smoothui.dev to see this and other visual components with incredible animations, especially useful if you are interested in creating interactive experiences like those from Apple applications.