An image viewer for React (+ Native). It works with any image component - bring your own image component (BYOICβ’)
galeria.shorter-2.mp4
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.
- 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 support
- 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
<FastImage />
fromreact-native-fast-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.
- @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"
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>
)
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>
)
import { Galeria } from '@nandorojo/galeria'
export const DarkMode = () => (
<Galeria urls={urls} theme="dark">
...
</Galeria>
)
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
style={styles.image}
source={src(item)}
recyclingKey={item + index}
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) // π€·ββοΈ
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>
)
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>
)
'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>
)
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>
)
import { Galeria } from '@nandorojo/galeria'
import FastImage from 'react-native-fast-image'
const urls = ['https://my-image.com/image.jpg']
export const FastImage = () => (
<Galeria urls={urls}>
<Galeria.Image>
<FastImage
source={{ uri: urls[0] }}
style={{ width: 100, height: 100 }}
/>
</Galeria.Image>
</Galeria>
)
yarn add @nandorojo/galeria
# or
npm i @nandorojo/galeria
Add @nandorojo/galeria
to transpilePackages
in your next.config.js
.
module.exports = {
transpilePackages: ['@nandorojo/galeria'],
}
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:android
- Under the hood, Galeria uses native libraries on iOS and Android.
- On Web, Galeria uses Framer Motion.
- 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.
