@nandorojo/galeria
v3.0.0
Published
A native galeria.
Maintainers
Readme
Galeria 📷
An image viewer for React (+ Native). It works with any image component - bring your own image component (BYOIC™)
https://github.com/user-attachments/assets/5062e949-b205-4260-830c-38041cec26db
The video makes Web look a bit weird, but don't worry. Here is why – it's just because of FlashList's Masonry List, which wraps the views with additional cells breaking z-index. But it isn't a fundamental Galeria issue.
Features
- Shared element transitions
- Pinch to zoom
- Double tap to zoom
- Pan to close
- Multi-image support
- React Native Modal support
- FlashList support
- Clean API
- Web support
- Remote URLs & local images
- New Architecture (Fabric) – required
- Supports different images when collapsed and expanded
- This lets you show smaller thumbnails with higher resolution expanded images
- Works with any image component
<Image />fromreact-native<SolitoImage />fromsolito/image<Image />fromnext/image<Image />fromexpo-image<img />on web- ...etc
For iOS and Android, the implementation uses Swift (ImageViewer.swift) and Kotlin (imageviewer) respectively – see credits.
Web support is a simplified version of the native experience powered by Framer Motion. It currently supports a single image at a time.
Resources
- @FernandoTheRojo's tweet about Galeria v1
- Watch my talk at App.js Conf about how to build Galeria
- "Don't be afraid to build a native library"
Usage
One Image
import { Galeria } from '@nandorojo/galeria'
import { Image } from 'react-native' // works with ANY image component!
const url = 'https://my-image.com/image.jpg'
export const SingleImage = ({ style }) => (
<Galeria urls={[url]}>
<Galeria.Image>
<Image source={{ uri: url }} style={style} />
</Galeria.Image>
</Galeria>
)Multiple Images
Simply pass an array to urls.
import { Galeria } from '@nandorojo/galeria'
import { Image } from 'react-native' // works with ANY image component!
import localImage from './assets/local-image.png'
const urls = ['https://my-image.com/image.jpg', localImage]
export const MutliImage = ({ style }) => (
<Galeria urls={urls}>
{urls.map((url, index) => (
<Galeria.Image index={index} key={...}>
<Image source={typeof url === 'string' ? { uri: url } : url} style={style} />
</Galeria.Image>
))}
</Galeria>
)Dark Mode
import { Galeria } from '@nandorojo/galeria'
export const DarkMode = () => (
<Galeria urls={urls} theme="dark">
...
</Galeria>
)FlashList
import { Galeria } from '@nandorojo/galeria'
import { Image, type ImageAssetSource } from 'react-native' // works with ANY image component!
import { FlashList } from '@shopify/flash-list'
import localImage from './assets/local-image.png'
const urls = ['https://my-image.com/image.jpg', localImage]
const size = 100
export const FlashListSupport = () => {
return (
<Galeria urls={urls}>
<FlashList
data={urls}
renderItem={({ item, index }) => {
// you should put this in a memoized component
return (
<Galeria.Image index={index}>
<Image
source={src(item)}
style={{ width: size, height: size }}
/>
</Galeria.Image>
)
}}
numColumns={3}
estimatedItemSize={size}
keyExtractor={(item, i) => item + i}
/>
</Galeria>
)
}
const src = (s) => (typeof s === 'string' ? { uri: s } : s) // 🤷♂️Low Resolution Thumbnails
You can use high resolution images when you tap, and low resolution as the collapsed thumbnail.
const lowResolutionUrls = createLowResolutionUrls(urls);
<Galeria urls={urls}>
{lowResolutionUrls.map((url, index) => (
<Galeria.Image index={index} key={...}>
<Image source={typeof url === 'string' ? { uri: url } : url} style={style} />
</Galeria.Image>
))}
</Galeria>Get Index of Currently Shown Image
iOS & Android
To get the index of the currently shown image in the image viewer use onIndexChange. It triggers on initial open of the image viewer and when the user scrolls through the images.
<Galeria urls={urls}>
{urls.map((url, index) => (
<Galeria.Image
index={index} key={...}
onIndexChange={(e) => setCurrentIndex(e.nativeEvent.currentIndex)}
>
<Image source={typeof url === 'string' ? { uri: url } : url} style={style} />
</Galeria.Image>
))}
</Galeria>Hide Blur Overlay
iOS only
Hide the blur overlay that appears behind the image viewer.
<Galeria.Image hideBlurOverlay>
<Image source={{ uri: url }} style={style} />
</Galeria.Image>Hide Page Indicators
iOS only
Hide the page indicator dots when viewing multiple images.
<Galeria.Image hidePageIndicators>
<Image source={{ uri: url }} style={style} />
</Galeria.Image>Plain Web Support
Galeria does not use any React Native code on the web. It is a pure React component library.
So you can even use <img /> if you want to only use it on web.
import { Galeria } from '@nandorojo/galeria'
const urls = ['https://my-image.com/image.jpg']
export const WebSupport = () => (
<Galeria urls={urls}>
<Galeria.Image>
<img src={urls[0]} width={100} height={100} />
</Galeria.Image>
</Galeria>
)Solito Image
import { SolitoImage } from 'solito/image'
const urls = ['https://my-image.com/image.jpg']
export const SolitoSupport = () => (
<Galeria urls={urls}>
<Galeria.Image>
<SolitoImage src={urls[0]} />
</Galeria.Image>
</Galeria>
)Next.js Image
'use client'
import { Galeria } from '@nandorojo/galeria'
import Image from 'next/image'
const urls = ['https://my-image.com/image.jpg']
export const NextJS = () => (
<Galeria urls={urls}>
<Galeria.Image>
<Image
src={urls[0]}
width={100}
height={100}
// edit these props for your use case
unoptimized
/>
</Galeria.Image>
</Galeria>
)Expo Image
import { Galeria } from '@nandorojo/galeria'
import { Image } from 'expo-image'
const urls = ['https://my-image.com/image.jpg']
export const ExpoImage = () => (
<Galeria urls={urls}>
<Galeria.Image>
<Image source={urls[0]} style={{ width: 100, height: 100 }} />
</Galeria.Image>
</Galeria>
)Installation
Requirements
- New Architecture (Fabric) – Galeria v3.0+ requires the new architecture. This means Expo SDK 54+ or React Native 0.79+.
- iOS 16+
For iOS 16+ deployment target:
- Bare RN: set it in
ios/Podfile - Expo: set it via
expo-build-properties
yarn add @nandorojo/galeria
# or
npm i @nandorojo/galeriaNext.js / Solito
Add @nandorojo/galeria to transpilePackages in your next.config.js.
module.exports = {
transpilePackages: ['@nandorojo/galeria'],
}Expo
Galeria uses native libraries on iOS and Android, so it does not work with Expo Go. You will need to use a dev client.
After installing it, rebuild your native code:
npx expo prebuild
npx expo run:ios # or npx expo run:androidCredits
- Under the hood, Galeria uses native libraries on iOS and Android.
- On Web, Galeria uses Framer Motion.
- Thanks to Luke Zhao for DynamicTransition
- Thanks to Michael Henry for the iOS Image Viewer
- Thanks to iielse for the Android Image Viewer
- Thanks to Alan for building the Android integration.
