Skip to main content

PhotoAlbum

Render responsive photo rows, columns, or masonry with an included lightbox.

PhotoAlbum lays out one photo collection. Selecting a thumbnail opens the included lightbox unless the lightbox is disabled or owned by a parent group.

Minimal usage

app/pages/gallery.vue
<script setup lang="ts">
import type { PhotoItem } from '@lupinum/nuxt-photo/app'

const photos: PhotoItem[] = [
  { id: 'one', src: '/photos/one.jpg', width: 1200, height: 800, alt: 'Mountain valley' },
  { id: 'two', src: '/photos/two.jpg', width: 800, height: 1200, alt: 'Forest path' },
]
</script>

<template>
  <PhotoAlbum :photos="photos" layout="rows" />
</template>

Props

PropTypeDefaultDescription
photosreadonly PhotoItem[]-Required. Ordered photos to render.
validation'throw' | 'drop''throw'Invalid collection policy.
layout'rows' | 'columns' | 'masonry' | AlbumLayout'rows'Layout type and its options.
spacingResponsiveParameter<number>8Gap between items in pixels.
paddingResponsiveParameter<number>0Padding around each item in pixels.
defaultContainerWidthnumber-Assumed server-side container width.
breakpointsreadonly number[]derivedWidth snap points; derived from responsive() when omitted.
sizesstring | ResponsivePhotoSizes-Native or rows-layout-aware image sizes hint.
imageAdapterImageAdapterprovided/module defaultOverride image delivery for this album.
lightboxboolean | ComponenttrueEnable, disable, or replace the lightbox.
transitionLightboxTransitionOption'auto'Lightbox transition configuration.
itemClassstring-Classes for each item wrapper.
imgClassstring-Classes for each image.

Rows layout accepts targetRowHeight and defaults it to 300. Columns and masonry accept columns and default it to 3. These values may use ResponsiveParameter<number>.

Events

EventPayloadWhen it fires
invalidPhotosInvalidPhotosEventInvalid entries are found while validation="drop" is set.
app/components/ValidatedAlbum.vue
<script setup lang="ts">
import type { InvalidPhotosEvent, PhotoItem } from '@lupinum/nuxt-photo/app'

defineProps<{ photos: readonly PhotoItem[] }>()

function reportInvalidPhotos(event: InvalidPhotosEvent) {
  console.warn('Invalid photos:', event.issues)
}
</script>

<template>
  <PhotoAlbum :photos="photos" validation="drop" @invalid-photos="reportInvalidPhotos" />
</template>

Slots

SlotPropsPurpose
thumbnail{ photo, index, width, height, hidden }Replace each thumbnail content.

hidden becomes true while the selected thumbnail is represented by the opening or closing animation. Use PhotoImage inside a custom thumbnail to keep the configured image adapter.

Exposed template-ref API

The component exposes LightboxHandle:

ts
interface LightboxHandle {
  open(index?: number): Promise<void>
  openById(id: string): Promise<void>
  close(): Promise<void>
  readonly isOpen: boolean
}

Inside PhotoGroup, an album-local index is translated to stable photo ID before the request is delegated to the group.

Important behavior

  • Rows produce justified photo rows. Columns keep a fixed column count. Masonry places each next photo in the shortest column.
  • Layout options, photos, transition configuration, and responsive values are reactive.
  • lightbox is setup-time. Remount with a new key to add, remove, or replace it.
  • A parent PhotoGroup owns the lightbox and navigation collection.
  • Incorrect dimensions can produce incorrect layout and transition geometry.

Responsive columns

vue
<PhotoAlbum
  :photos="photos"
  :layout="{
    type: 'columns',
    columns: responsive({ 0: 2, 640: 3, 1024: 4 }),
  }"
  :spacing="responsive({ 0: 4, 640: 8, 1024: 12 })"
/>

Nuxt auto-imports responsive(). Plain Vue applications import it from @lupinum/vue-photo.

Use Tune responsive layouts for layout decisions and SSR and layout stability for defaultContainerWidth and hydration behavior.