svelte-gallery-view
v1.0.1
Published
A collection of Svelte components for displaying photos, featuring a Google Photos / 500px style layout.
Maintainers
Readme
Svelte Gallery View
A collection of Svelte 5 components for displaying photos, featuring a Google Photos / 500px style layout that keeps every row the same height while preserving each photo's aspect ratio.

v1.0.0 requires Svelte 5 and is written with runes (
$props/$state/$effect). For Svelte 3/4, use the 0.x releases. See Upgrading from 0.x.
Table of Contents
- Features
- Installation
- Quick start
- API
- Styling
- Lazy loading
- Upgrading from 0.x
- Developing
- Testing
- Packaging & publishing
- Contributing
- License
Features
- Equal-height rows, original aspect ratios — the Google Photos / 500px layout, implemented in pure CSS (inspired by Pure CSS implementation of Google Photos / 500px image layout).
- Lazy loading — each image starts as a lightweight placeholder and only loads when it scrolls into view.
- Async photo URLs — a photo
urlmay be aPromise<string>; the image resolves once the promise settles (e.g. after signing a URL). - SSR-friendly — lazy-loading effects are client-only and never run during server rendering or prerendering.
- Zero runtime dependencies — only Svelte 5 in
peerDependencies.
The demo app in this repository shows 47 photos rendered with GalleryView at localhost:7070 (pnpm dev).
Installation
Requires Svelte 5:
npm install svelte-gallery-viewQuick start
<script lang="ts">
import GalleryView, { type Photo } from 'svelte-gallery-view';
const photos: Photo[] = [
{
url: 'https://example.com/photo-1.jpg',
width: 320,
height: 213,
title: 'Photo 1',
},
{ url: 'https://example.com/photo-2.jpg', width: 320, height: 480 },
// ...
];
</script>
<GalleryView
{photos}
gutter={2}
baseHeight={200}
photoClass="photo"
onPhotoClick={(photo) => showPhotoDetails(photo.id)}
/>API
<GalleryView> props
| Prop | Type | Default | Description |
| -------------- | ------------------------ | ----------- | --------------------------------------------------------------------------------------------- |
| photos | Photo[] | [] | Photos to display. Required — without it nothing renders. |
| baseHeight | number | 200 | Base height of each row, in px. The final height of each row may vary because of adjustments. |
| gutter | number | 2 | Gap between every two photos, in px. |
| photoClass | string | undefined | Extra class name(s) applied to each photo wrapper element. |
| onPhotoClick | (photo: Photo) => void | undefined | Callback invoked with the clicked photo. |
Photo
interface Photo {
/** URL or unresolved (Promise) URL of the photo. */
url: string | Promise<string>;
/** Width of the photo, in px. */
width: number;
/** Height of the photo, in px. */
height: number;
/** Extra information shown as a tooltip text on the photo. */
title?: string;
/** Any extra properties you want available in the onPhotoClick callback. */
[key: string]: unknown;
}You can import the Photo type together with the component:
import GalleryView, { type Photo } from 'svelte-gallery-view';Styling
photoClassis applied to every photo wrapper element (a<div class="image …">), so it is the right place for hover effects, border radius and the like:
.photo {
border-radius: 4px;
overflow: hidden;
transition: transform 150ms ease;
}
.photo:hover {
transform: translateY(-2px);
}- Each wrapper also exposes CSS custom properties that you can target with
:global:--gutter(on the section), and--ratio,--width,--flex-grow(per photo). The inner image element carries theimgandlazyloadclasses (see GalleryView.svelte).
Lazy loading
Each photo renders a transparent 1×1 placeholder first. When the photo scrolls into view, an IntersectionObserver swaps in the real image. If url is a Promise, the swap happens when it resolves; if it rejects, the placeholder stays.
Upgrading from 0.x
The component API is unchanged — only the runtime requirement changed:
[email protected]requires Svelte 5 (declared inpeerDependencies).- Reinstall:
npm install svelte-gallery-view@^1.0.0— keep your existing<GalleryView>usage as-is. - Internally the components were rewritten with runes; behavior (layout, placeholder, click callback) is identical.
See CHANGELOG.md for the full 1.0.0 notes.
Developing
- Install dependencies:
pnpm install - Start the demo app (Vite + SvelteKit):
pnpm dev - Edit the components in
src/liband the browser hot-reloads - Navigate to localhost:7070
Testing
Component tests are written with Vitest and Testing Library, and run in jsdom.
pnpm testOther quality gates:
pnpm check # svelte-check (types + a11y)
pnpm lint # ESLint (flat config)
pnpm format # PrettierPackaging & publishing
Build the distributable package into dist/ (ESM source components + generated type declarations, via @sveltejs/package). The components are shipped as runes source, so consumers compile them with their own Svelte 5 + Vite build:
pnpm package
npm pack --dry-run # review the tarball before publishingThe package is published to npm as svelte-gallery-view:
Local — after reviewing the tarball:
npm publishGitHub Actions — push a version tag (e.g.
v1.0.1); the publish workflow builds and publishes with provenance. Requires anNPM_TOKENsecret with publish scope.
Contributing
Contributions are welcome. Please review our CONTRIBUTING.md and CODE-OF-CONDUCT.md first.
