Building an Interactive Image Gallery in the Style of the iPhone Photos App with Next.js and Framer Motion

Learn how to build an interactive image gallery inspired by Apple's iPhone Photos app with Next.js, React, TypeScript, and Framer Motion, including image selection, deletion, and smooth animations.

Published on September 5, 2024

Views
100 views
Reading time:
Reading time: 5 min

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
Galería interactiva framer-motion, apple style

##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.div to animate the entire grid.
  • AnimatePresence is used to animate the entry and exit of list elements.
  • Each image is wrapped in a motion.div with 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:

  1. Project setup and state management with React Hooks.
  2. Implementation of a responsive and attractive user interface.
  3. Using Framer Motion for smooth and natural animations.
  4. 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.