---
title: "ImageGallery"
description: "A thumbnail grid with a fullscreen lightbox preview."
category: Data
status: beta
since: 0.3.4
tags: [image, gallery, preview]
syncStatus: reviewed
verified: true
---

## Usage

### Basic
Clicking a thumbnail opens it in a fullscreen lightbox; `startIndex` picks the current preview image.
::::TuffDemoWrapper{demo="ImageGalleryImageGalleryDemo" code-lang="vue"}
---
code: |
  <script setup lang="ts">
  const items = [
    { id: 'one', url: '/images/one.png', name: 'Workspace overview' },
    { id: 'two', url: '/images/two.png', name: 'Release graph' },
  ]
  </script>

  <template>
    <TxImageGallery :items="items" :start-index="1" />
  </template>
---
::::

### Track Preview Opens

```vue
<script setup lang="ts">
function onOpen({ index, item }: { index: number, item: { id: string } }) {
  analytics.track('gallery_open', { index, imageId: item.id })
}
</script>

<template>
  <TxImageGallery :items="images" @open="onOpen" @close="onClose" />
</template>
```

### Controlled Starting Image
`startIndex` only selects the preview index; it never opens the lightbox.

```vue
<template>
  <TxImageGallery :items="screenshots" :start-index="selectedIndex" />
</template>
```

### Best Practices

- Use stable `id` values, not array indexes, for long-lived gallery data.
- Give informative images a `name` so button labels, alt text, and preview titles mean something.
- Keep the list modest: every thumbnail renders, with no virtualization.
- Normalize or proxy untrusted remote image URLs at your application boundary.
- Don't mutate `items` synchronously in `@open` in a way that invalidates the opened image.

## API Reference

### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `items` | `ImageGalleryItem[]` | required | Images shown as thumbnails and in the fullscreen preview. |
| `startIndex` | `number` | `0` | Current preview index, clamped to the item range. |
| `previousLabel` | `string` | `'Previous image'` | Accessible name of the previous button. |
| `nextLabel` | `string` | `'Next image'` | Accessible name of the next button. |
| `previousText` | `string` | `'Prev'` | Visible text of the previous button. |
| `nextText` | `string` | `'Next'` | Visible text of the next button. |
| `previewTitle` | `string` | `'Preview'` | Preview title when the current image has no `name`. |
| `itemLabelFormatter` | `(index: number) => string` | `` (i) => `Image ${i + 1}` `` | Builds a display name when `item.name` is absent. |
| `openLabelFormatter` | `(label: string) => string` | `` (label) => `Open ${label} preview` `` | Builds a thumbnail's `aria-label` from its display name. |

### Events

| Event | Payload | Description |
|------|---------|-------------|
| `open` | `{ index: number, item: ImageGalleryItem }` | Fires after a thumbnail opens the fullscreen preview. |
| `close` | `void` | Fires when the preview closes. |

### ImageGalleryItem

| Field | Type | Description |
|------|------|-------------|
| `id` | `string` | Stable key for the thumbnail. |
| `url` | `string` | Image URL for the thumbnail and the preview. |
| `name` | `string` | Optional display name for labels, alt text, and the preview title. |

## Overview

- Each thumbnail is a native `<button type="button">` whose `aria-label` comes from `openLabelFormatter`.
- Thumbnail and preview `alt` text is `item.name`; unnamed images deliberately get empty alt text.
- The lightbox traps Tab focus, closes on Escape or the close button, and returns focus to the thumbnail that opened it.
- Navigation is bounded: previous is disabled on the first image, next on the last; when a button disables, focus moves to the other.
- The image is contained, never cropped, between the header and footer bars.
- When `items` becomes empty, the preview closes and the index resets to `0`.

## Technologies

- The lightbox is `TxModal` in `fullscreen` mode (teleported to `body`), not a separate overlay.
- Source: `packages/tuffex/packages/components/src/image-gallery/`.

<TuffDocSourceLink />
