@hexidecibel/gallery
v0.1.0
Published
A beautiful, configurable photo gallery component for React with multiple layout options and source adapters
Downloads
8
Maintainers
Readme
@hexi/gallery
A beautiful, configurable photo gallery component for React with grid, masonry, and justified layouts.
- Three layout algorithms — CSS Grid, Masonry (shortest-column), and Justified (Google Photos-style row packing)
- Full-featured lightbox — keyboard navigation, touch swipe, pinch-to-zoom, focus trap, portal-based
- Source adapters — URL arrays, S3 bucket listings, async paginated sources, local file uploads
- Performance — lazy loading with IntersectionObserver, LQIP blur placeholders, optional virtualization for 200+ images
- Interactive configurator — visual builder with code export
- Accessible — ARIA labels, focus management, reduced motion support, WCAG AA contrast
- Themeable — 30+ CSS custom properties, dark mode, custom themes
- Zero dependencies — only React 18 as a peer dependency
- TypeScript — full type definitions, generic adapter interface
Installation
npm install @hexi/galleryQuick Start
import { Gallery } from '@hexi/gallery';
import '@hexi/gallery/styles';
const images = [
{ id: '1', src: '/photos/beach.jpg', alt: 'Beach sunset', width: 800, height: 600 },
{ id: '2', src: '/photos/mountains.jpg', alt: 'Mountain vista', width: 1200, height: 800 },
{ id: '3', src: '/photos/forest.jpg', alt: 'Forest trail', width: 600, height: 900 },
];
function App() {
return (
<Gallery
images={images}
layout={{ type: 'masonry', gap: 12 }}
enableLightbox
/>
);
}Layouts
Grid
Responsive CSS Grid with auto-fill columns.
<Gallery images={images} layout={{ type: 'grid', gap: 16, columns: 'auto' }} />Masonry
Pinterest-style shortest-column distribution.
<Gallery images={images} layout={{ type: 'masonry', gap: 12, columns: 4 }} />Justified
Google Photos-style row packing that fills the container width.
<Gallery images={images} layout={{ type: 'justified', gap: 8, rowHeight: 240 }} />Source Adapters
URL Adapter
import { urlAdapter, useSourceAdapter } from '@hexi/gallery';
function MyGallery() {
const { images, loading } = useSourceAdapter(urlAdapter, {
urls: ['https://example.com/a.jpg', 'https://example.com/b.jpg'],
});
if (loading) return <p>Loading...</p>;
return <Gallery images={images} enableLightbox />;
}S3 Adapter
Fetches public S3 bucket listings. Accepts a custom fetchFn for authenticated access — no AWS SDK dependency.
import { s3Adapter, useSourceAdapter } from '@hexi/gallery';
const { images } = useSourceAdapter(s3Adapter, {
bucket: 'my-photos',
region: 'us-east-1',
prefix: 'gallery/',
});Async Adapter
Paginated sources with loadMore().
import { useAsyncAdapter } from '@hexi/gallery';
const { images, loadMore, hasMore } = useAsyncAdapter(async (cursor) => {
const res = await fetch(`/api/photos?cursor=${cursor || ''}`);
const data = await res.json();
return { images: data.items, nextCursor: data.next, hasMore: data.hasNext };
});Local Files
import { createImagesFromFiles } from '@hexi/gallery';
function handleFiles(e: React.ChangeEvent<HTMLInputElement>) {
const images = createImagesFromFiles(e.target.files!);
setImages(images);
}Theming
Override CSS custom properties to customize appearance:
:root {
--hexi-bg-primary: #0a0a0a;
--hexi-image-radius: 16px;
--hexi-shadow-md: 0 4px 20px rgba(0, 100, 255, 0.15);
--hexi-gap-md: 12px;
}Dark mode is supported automatically via prefers-color-scheme.
Configurator
The interactive configurator lets users visually build gallery configurations and export the code:
import { ConfiguratorPanel, useConfigurator } from '@hexi/gallery';
function Builder() {
const config = useConfigurator();
return (
<ConfiguratorPanel
state={config.state}
exportCode={config.exportCode}
onLayoutChange={config.setLayout}
onLayoutTypeChange={config.setLayoutType}
onImagesChange={config.setImages}
onThemeChange={config.setTheme}
onReset={config.reset}
/>
);
}Virtualization
For galleries with many images, enable virtualization to only render visible items:
<Gallery images={largeImageSet} virtualize enableLightbox />Supported for Grid and Masonry layouts. Justified layout requires the full image list for row composition.
API
<Gallery> Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| images | ImageItem[] | required | Array of images |
| layout | LayoutOptions | { type: 'grid', columns: 'auto', gap: 16 } | Layout configuration |
| enableLightbox | boolean | false | Enable built-in lightbox |
| loading | 'lazy' \| 'eager' | 'lazy' | Image loading strategy |
| virtualize | boolean \| number | false | Enable virtualization |
| onImageClick | (image, index) => void | — | Click handler |
| className | string | — | Additional CSS class |
| renderImage | (image, index) => ReactNode | — | Custom render function |
Hooks
| Hook | Description |
|------|-------------|
| useLightbox | Lightbox state management |
| useIntersectionObserver | Lazy loading with IntersectionObserver |
| useContainerWidth | ResizeObserver-based width measurement |
| usePinchZoom | Pinch-to-zoom with touch events |
| useSourceAdapter | Generic source adapter hook |
| useAsyncAdapter | Paginated async adapter with loadMore() |
| useConfigurator | Configurator state management |
| useLocalStorage | Persisted state via localStorage |
| useGalleryPerf | Performance monitoring metrics |
| useVirtualization | Window-scroll virtualization |
Development
npm install
npm run dev # Start dev server
npm run test # Run tests (watch mode)
npm run test:run # Run tests (single run)
npm run build # Production build
npm run lint # ESLintLicense
MIT
