Blueprint

Gallery

Image gallery component that displays image thumbnails in a grid and full size images in a modal.

React

Gallery renders a grid of thumbnails that open a modal of the full size image when clicked. For more advanced control over the behavior, you can pass isOpen to put the Gallery into controlled mode to manually open and close it. For an even more custom layout or behavior see useGallery.

Images are specified as a list of Image objects with at least a url property.


interface Image {
url: string;
thumbnail?: string;
title?: string;
}

The title and thumbnail properties are optional, but a thumbnail is highly recommended so that the full size image doesn't need to be loaded just to display the thumbnail grid.

Installation

The Gallery component is available as a separate package, @hover/gallery that depends on @hover/blueprint.


yarn add @hover/gallery

npm

Theme

The theme for the Gallery component must also be added to your DesignSystemProvider.

App.tsx

import { DesignSystemProvider } from '@hover/blueprint';
import { Gallery } from '@hover/blueprint/extra/theme';
interface AppProps {
children: React.ReactNode;
}
const App = ({ children }: AppProps) => (
<DesignSystemProvider extraComponentThemes={{ Gallery }}>
{children}
</DesignSystemProvider>
);
export { App };

Thumbnails

The thumbnail grid composes SimpleGrid, so simply pass SimpleGrid props to control the layout of the thumbnails.

Note

The number of columns is determined by the columns prop or minChildWidth — they are not intended to be used together. See the SimpleGrid documentation for details.

live

<Gallery
images={[
{
url: '/images/kitten--300-300.jpg',
thumbnail: '/images/kitten--300-300_thumb.jpg',
title: 'A wonderful title',
},
{
url: '/images/kitten--400-400.jpg',
thumbnail: '/images/kitten--400-400_thumb.jpg',
title: "Next one won't have a title",
},
{
url: '/images/kitten--400-300.jpg',
thumbnail: '/images/kitten--400-300_thumb.jpg',
},
{
url: '/images/kitten--300-400.jpg',
thumbnail: '/images/kitten--300-400_thumb.jpg',
},
]}
minChildWidth="400"
spacing="400"
title="A Gallery Title"
width="800"
/>

Hover

Set showHover to false to disable the thumbnail hover state;

live

<Gallery
columns={3}
images={[
{
url: '/images/kitten--300-300.jpg',
thumbnail: '/images/kitten--300-300_thumb.jpg',
title: 'A wonderful title',
},
{
url: '/images/kitten--400-400.jpg',
thumbnail: '/images/kitten--400-400_thumb.jpg',
title: "Next one won't have a title",
},
{
url: '/images/kitten--400-300.jpg',
thumbnail: '/images/kitten--400-300_thumb.jpg',
},
]}
showHover={false}
spacing={400}
title="A Gallery Title"
width="750"
/>

Size

Set size to control the size of the modal.

live

<Gallery
size="auto"
columns={3}
images={[
{
url: '/images/kitten--300-300.jpg',
thumbnail: '/images/kitten--300-300_thumb.jpg',
title: 'A wonderful title',
},
{
url: '/images/kitten--400-400.jpg',
thumbnail: '/images/kitten--400-400_thumb.jpg',
title: "Next one won't have a title",
},
{
url: '/images/kitten--1920-1080.jpg',
thumbnail: '/images/kitten--1920-1080_thumb.jpg',
},
]}
showHover={false}
spacing={400}
title="Auto Sizing Gallery"
width="750"
/>

Fullscreen

An alternative version of the GalleryModal component is available, FullscreenGalleryModal, which renders the modal as a fullscreen overlay and supports scroll-based zoom on high resolution images.

live

<Gallery
images={[
{
url: '/images/kitten--1920-1080.jpg',
thumbnail: '/images/kitten--1920-1080_thumb.jpg',
},
{
url: '/images/kitten--2560-1440.jpg',
thumbnail: '/images/kitten--2560-1440_thumb.jpg',
},
{
url: '/images/kitten--1080-1920.jpg',
thumbnail: '/images/kitten--1080-1920_thumb.jpg',
},
]}
>
<FullscreenGalleryModal
title="Fullscreen Gallery"
zoomOptions={{ maxZoom: 5 }}
/>
<GalleryGrid columns={2} spacing="300" width="700" />
</Gallery>

Limit

Pass the limit prop to collapse the thumbnail list to only display the specified number of thumbnails. When passing limit, the isExpanded controlled prop is required to control the expanded state of the thumbnail list.

Info

For some reason this example doesn't work with the live editor


() => {
const [isExpanded, { toggle }] = useBoolean(false);
return (
<VStack
bg="neutral.50"
borderRadius="500"
p="300"
shadow="inset100"
spacing="300"
width="700"
>
<Gallery
columns={2}
images={[
{
url: '/images/kitten--300-300.jpg',
thumbnail: '/images/kitten--300-300_thumb.jpg',
},
{
url: '/images/kitten--400-400.jpg',
thumbnail: '/images/kitten--400-400_thumb.jpg',
},
{
url: '/images/kitten--400-300.jpg',
thumbnail: '/images/kitten--400-300_thumb.jpg',
},
{
url: '/images/kitten--500-150.jpg',
thumbnail: '/images/kitten--500-150_thumb.jpg',
},
{
url: '/images/kitten--300-400.jpg',
thumbnail: '/images/kitten--300-400_thumb.jpg',
},
]}
isExpanded={isExpanded}
limit={2}
spacing="300"
title="A Gallery Title"
width="100%"
/>
<Button
iconBefore={isExpanded ? iArrowUp : iArrowDown}
onClick={toggle}
shape="box"
width="100%"
>
{isExpanded ? 'Collapse' : 'Expand'}
</Button>
</VStack>
);
};

useGalleryContext

For full control over any part of the Gallery interface, you can pass custom children that read the gallery state from useGalleryContext.

live

() => {
const CustomThumbnails = () => {
const { thumbnails, open } = useGalleryContext();
return (
<HStack>
{thumbnails.map(({ url }, i) => (
<img
key={url}
onClick={() => open(i)} // Don't actually do this, `onClick` on an `img` is not accessible
src={url}
style={{ height: '100px' }}
/>
))}
</HStack>
);
};
return (
<Gallery
spacing="100"
images={[
{
url: '/images/kitten--300-300.jpg',
thumbnail: '/images/kitten--300-300_thumb.jpg',
},
{
url: '/images/kitten--400-400.jpg',
thumbnail: '/images/kitten--400-400_thumb.jpg',
},
{
url: '/images/kitten--400-300.jpg',
thumbnail: '/images/kitten--400-300_thumb.jpg',
},
]}
spacing={400}
title="A Gallery Title"
>
<CustomThumbnails />
<GalleryModal />
</Gallery>
);
};

Custom Renders

Set renderSlideContent to render a custom component instead of an image. The function takes the current image as parameter, whose type is defined as the generic TImage.

live

<Gallery
size="full"
columns={3}
images={[
{
url: '/images/kitten--300-300.jpg',
title: 'A wonderful title',
tags: ['whiskers', 'paws'],
},
{
url: '/images/kitten--400-400.jpg',
title: "Next one won't have a title",
tags: ['whiskers', 'tail'],
},
{
url: '/images/kitten--1920-1080.jpg',
tags: ['tail'],
},
]}
showHover={false}
spacing={400}
title={image => image.title}
renderSlideContent={image => (
<Box p={500}>
<Box>
<Image
alt={image.title ?? ''}
draggable={false}
maxW="100%"
src={image.url}
/>
</Box>
<Box flexFlow="column" gap={300} px={500} width={700}>
<Box gap={100}>
{image.tags.map(tag => (
<Badge key={tag}>{tag}</Badge>
))}
</Box>
{image.title}
</Box>
</Box>
)}
/>


Copyright © 2024 Hover Inc. All Rights Reserved.