Skip to main content

Control the lightbox from code

Open the lightbox at a given photo from a button, bind the active photo with v-model:active, close it with the browser Back button, link to a photo with deepLink, and move next and previous with template refs or useLightbox().

There are two ways to control a lightbox from your code:

  • A template ref on any ready-made component opens and closes it.
  • useLightbox() in a child component also moves between photos and reads the current state.

Open a photo from a button

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

const props = defineProps<{ photos: PhotoItem[] }>()
const album = useTemplateRef<GalleryHandle>('album')
</script>

<template>
  <button type="button" :disabled="props.photos.length < 5" @click="album?.open(4)">
    Open the fifth photo
  </button>
  <PhotoAlbum ref="album" :photos="photos" />
</template>

Indexes start at 0. openById('canyon') opens a photo by its id, which stays right when the order changes. Both reject with a RangeError for an index or ID that does not exist.

Every ready-made component exposes the same handle:

ts
interface GalleryHandle {
  open(index?: number): Promise<void>
  openById(id: string): Promise<void>
  close(): Promise<void>
  readonly isOpen: boolean
  readonly activeId: string | null
  readonly activePhoto: PhotoItem | null
}

For an album inside a PhotoGroup, open(index) counts in that album; the group's handle counts in the group. Photo accepts index 0 or its own ID. PhotoCarousel also exposes scrollTo(index), next(), and prev() for its inline track.

Bind the active photo

Use v-model:active on Photo, PhotoAlbum, PhotoGroup, or PhotoCarousel:

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

defineProps<{ photos: PhotoItem[] }>()
const active = ref<string | null>(null)
</script>

<template>
  <PhotoAlbum v-model:active="active" :photos="photos" />
</template>

Enable the lightbox before using the model. Photo and PhotoCarousel need the lightbox prop; albums and groups enable it by default.

Set an ID to open or navigate. Set null to close. User navigation updates the value. Unknown IDs are ignored with one development warning per component. The handle reports activeId and activePhoto as null when closed. For Photo, the model is its single ID or null.

Control the lightbox from a child component

useLightbox() returns the full controller of the nearest parent provider: PhotoGroup or LightboxProvider.

app/components/GalleryToolbar.vue
<script setup lang="ts">
import { useLightbox } from '@lupinum/nuxt-photo/app'

const lightbox = useLightbox()
</script>

<template>
  <div class="gallery-toolbar">
    <span v-if="lightbox.isOpen.value">
      {{ lightbox.activeIndex.value + 1 }} of {{ lightbox.count.value }}
    </span>
    <button type="button" @click="lightbox.prev()">Previous</button>
    <button type="button" @click="lightbox.next()">Next</button>
  </div>
</template>
app/pages/gallery.vue
<template>
  <PhotoGroup :photos="photos">
    <GalleryToolbar />
    <PhotoAlbum :photos="photos" />
  </PhotoGroup>
</template>

Call useLightbox() in a component rendered inside the provider, not in the component that renders the provider.

ts
interface LightboxController {
  readonly photos: ComputedRef<readonly PhotoItem[]>
  readonly count: ComputedRef<number>
  readonly activeId: ComputedRef<string | null>
  readonly activeIndex: ComputedRef<number>
  readonly activePhoto: ComputedRef<PhotoItem | null>
  readonly isOpen: ComputedRef<boolean>
  open(index?: number): Promise<void>
  openById(id: string): Promise<void>
  close(): Promise<void>
  next(): void
  prev(): void
  toggleZoom(): void
}

next() and prev() do nothing while the lightbox is closed. Calling close() twice is safe. The promises settle when the opening or closing animation has finished.

Close the lightbox with the Back button

The lightbox adds a browser history entry when it opens. Back closes the lightbox and leaves the page where it was: the page does not scroll, the router does not navigate, and route middleware does not run again. Closing with the close button or Escape removes the entry again, so a later Back leaves the page as usual.

This is on by default. Turn it off for one gallery:

vue
<PhotoAlbum :photos="photos" :lightbox="{ history: false }" />

Or for the whole app:

nuxt.config.ts
export default defineNuxtConfig({
  nuxtPhoto: { lightbox: { history: false } },
})

When several galleries are on one page, only the open lightbox writes history.

With deepLink, the URL shows the open photo, so a shared link opens it:

app/pages/gallery.vue
<template>
  <PhotoAlbum :photos="photos" :lightbox="{ deepLink: true }" />
</template>
  • While the lightbox is open, the URL has ?photo=<id>. Moving to another photo replaces the value; it does not add a history entry.
  • A page that loads with ?photo=<id> opens that photo without the opening animation, also after server rendering.
  • An ID that is not in this gallery is ignored.
  • deepLink: 'image' uses ?image=<id> instead. Give each gallery on a page its own name, so their links do not interfere.

IDs appear in shared URLs. Use IDs that are stable and contain nothing private.

Every lightbox option and its default is in the lightbox reference.