Component
Suitable for complex scenarios involving custom toolbars or images in dynamically rendered Markdown or HTML whose content is not known in advance. By default, it watches additions, removals and attribute changes of <img> elements inside the component and automatically updates the gallery.
Upgrading from an older major version
The Vue 3 (5.x) line is a rewrite with breaking changes. You can now use Viewer.js props, events and methods directly on the component. The legacy options prop is retained for compatibility, but is deprecated. Existing options-based galleries can migrate gradually.
Playground
<script setup>
import { ref } from 'vue'
import { Viewer } from 'v-viewer'
const sourceImages = [
'/images/photo-01.jpg',
'/images/photo-02.jpg',
'/images/photo-03.jpg',
'/images/photo-04.jpg',
'/images/photo-05.jpg',
'/images/photo-06.jpg',
'/images/photo-07.jpg',
'/images/photo-08.jpg',
'/images/photo-09.jpg',
'/images/photo-10.jpg',
].map((original) => ({
src: original.replace('/images/', '/images/thumbnails/'),
'data-source': original,
}))
const images = ref(sourceImages.slice(0, 5))
const toolbar = ref(true)
function onViewed(event) {
console.log(event.detail.index)
}
const gallery = ref(null)
function show() {
gallery.value.show()
}
function add() {
images.value.push(sourceImages[images.value.length])
}
function remove() {
images.value.pop()
}
</script>
<template>
<main class="viewer-example">
<div class="controls">
<button :disabled="images.length === sourceImages.length" @click="add">
Add image
</button>
<button :disabled="images.length === 0" @click="remove">
Remove image
</button>
<button class="open" :disabled="images.length === 0" @click="show()">
Show viewer
</button>
<div class="toolbar" role="group" aria-label="Toggle toolbar">
<span>Toggle toolbar</span>
<button
:aria-pressed="toolbar ? 'true' : 'false'"
@click="toolbar = true"
>
On
</button>
<button
:aria-pressed="toolbar ? 'false' : 'true'"
@click="toolbar = false"
>
Off
</button>
</div>
</div>
<Viewer
ref="gallery"
:toolbar="toolbar"
url="data-source"
@viewed="onViewed"
class="gallery"
><img
v-for="(image, index) in images"
:key="image.src"
:src="image.src"
:data-source="image['data-source']"
:alt="`Landscape ${index + 1}`"
/></Viewer>
</main>
</template>Props
Viewer.js props
These props map to Viewer.js options; click a name for its full documentation. Availability and defaults follow the installed Viewer.js version. Changes, including nested values, rebuild the instance after DOM updates.
| Prop | Type | Purpose |
|---|---|---|
backdrop | boolean / string | Control the modal backdrop; static keeps clicks on it from closing the viewer. |
button | boolean | Show the button in the upper-right corner. |
className | string | Add custom classes to the viewer root. |
container | string / Element | Choose the modal container using an element or CSS selector. |
filter | function | Include images for which the filter returns true; exclude the others. |
fullscreen | boolean / FullscreenOptions | Request browser fullscreen during playback, optionally with Fullscreen API settings. |
focus | boolean | Focus the active thumbnail on initialization; requires keyboard. |
inheritedAttributes | array | Choose additional source-image attributes to copy; src and alt are always copied. |
initialCoverage | number | Set the initial image coverage, greater than 0 and at most 1. |
initialViewIndex | number | Choose the starting image index, also used when view() receives no index. |
inline | boolean | Display the viewer inline when enabled, or in modal mode when disabled. |
autoplay | boolean | Advance slides automatically during playback. |
interval | number | Set the delay between slides in milliseconds. |
keyboard | boolean | Allow keyboard controls. |
loading | boolean | Display a loading indicator while fetching an image. |
loop | boolean | Wrap image navigation between the first and last images. |
magnifier | boolean / object | Show a hover magnifier in fullscreen view, with configurable size, ratio and opacity; unavailable on touch screens. |
maxZoomRatio | number / function | Limit magnification with a ratio or a function of the image and its data. |
minHeight | number | Set the minimum inline viewer height in pixels. |
minWidth | number | Set the minimum inline viewer width in pixels. |
minZoomRatio | number / function | Limit reduction with a ratio or a function of the image and its data. |
movable | boolean | Allow image movement. |
navbar | boolean / number / string / object | Configure thumbnail visibility, size and position, with further layout settings in object form. |
navigation | boolean / number / object | Configure the side navigation buttons, including individual visibility and size. |
preload | boolean | Load adjacent images before they are viewed. |
rotatable | boolean | Allow image rotation. |
rotateOnGesture | boolean | Rotate through gesture events, such as two-finger gestures in Mac Safari. |
rotateOnTouch | boolean | Rotate with two fingers on a touch screen. |
scalable | boolean | Allow axis scaling, including horizontal and vertical flips. |
slideOnTouch | boolean | Navigate between images with touch swipes. |
slideOnWheel | boolean / string | Navigate with the wheel, optionally requiring modifier keys; applies over thumbnails or when wheel zoom does not handle the event. |
title | boolean / number / function / array | Configure title visibility and content; the default uses alt or the filename from the URL. |
toggleOnDblclick | boolean | Switch between natural and initial image sizes on double-click. |
toolbar | boolean / number / object | Configure toolbar placement and button visibility, size and click behavior. |
tooltip | boolean | Display the current zoom percentage while zooming. |
transition | boolean / object | Enable CSS transitions, or disable selected action transitions using an object. |
url | string / function | Read the full image URL from a named image attribute or a function result. |
zIndex | number | Set the modal viewer’s CSS stacking order. |
zIndexInline | number | Set the inline viewer’s CSS stacking order. |
zoomOnGesture | boolean | Zoom through gesture events, such as pinch gestures in Mac Safari. |
zoomOnTouch | boolean | Zoom through dragging gestures on a touch screen. |
zoomOnWheel | boolean / string | Zoom with the wheel, optionally requiring one or more modifier keys. |
zoomRatio | number | Set the ratio increment for wheel zoom. |
zoomable | boolean | Allow image zooming. |
v-viewer-specific props
| Prop | Type | Purpose |
|---|---|---|
options | ViewerOptions | Default {}. Deprecated compatibility entry; prefer direct props and Vue events. |
rebuild | boolean | false; rebuild instead of updating when images change. |
observe | boolean / number | Default true (50ms); false disables observation; a non-negative number sets the batching window in milliseconds. |
The component observes additions, removals and attribute changes of <img> elements inside it, including whole HTML subtree replacements. It calls update() by default, or recreates the instance when rebuild is enabled.
See automatic updates with observe for values, update timing and usage guidance.
Events
These are Viewer.js events exposed as Vue events. Click an event name for its original documentation.
Event handlers receive the original DOM event, with operation data in event.detail. Use @show.prevent or event.preventDefault() to cancel a cancelable operation.
| Name | When it fires / useful data |
|---|---|
ready | The viewer UI is initialized. In modal mode this can happen on the first opening. |
show / shown | Before opening / after the opening transition completes in modal mode. |
hide / hidden | Before closing / after the closing transition completes in modal mode. |
view / viewed | Before displaying an image / after it is displayed. detail.index is zero-based; image and originalImage identify the preview and source images. |
move / moved | Before / after moving. detail.x, y, oldX and oldY describe the position change. |
rotate / rotated | Before / after rotation. detail.degree and oldDegree describe the angle change. |
scale / scaled | Before / after scaling or flipping. detail.scaleX, scaleY, oldScaleX and oldScaleY describe the change. |
zoom / zoomed | Before / after zooming. detail.ratio and oldRatio contain the zoom ratios. |
play / stop | Before starting / stopping a slideshow. |
playing | Before showing each slide during playback. |
Callbacks
The legacy options prop still accepts Viewer.js callbacks such as options.viewed. This compatibility entry is deprecated; prefer Vue events such as @viewed. Callbacks receive the same original DOM event as the component event. When both are supplied, both run.
Methods
Viewer.js methods
Call these through the component ref. Each name links to the corresponding Viewer.js method.
| Method | Purpose |
|---|---|
show(immediate?) | Open the modal, immediately when passed true; modal mode only. |
hide(immediate?) | Close the modal, immediately when passed true; modal mode only. |
view(index?) | View the indexed image, defaulting to initialViewIndex; open the viewer first if hidden. |
prev(loop?) | View the previous image; loop allows wrapping to the last image and defaults to false. |
next(loop?) | View the next image; loop allows wrapping to the first image and defaults to false. |
move(x, y?) | Move by relative offsets; omitted y uses x. |
moveTo(x, y?) | Move to absolute coordinates; omitted y uses x. |
rotate(degree) | Rotate by a relative angle: positive turns right, negative turns left. |
rotateTo(degree) | Set an absolute rotation angle. |
scale(scaleX, scaleY?) | Scale both axes; omitted scaleY uses scaleX, and negative factors flip the image. |
scaleX(scaleX) | Scale horizontally; -1 flips the horizontal axis. |
scaleY(scaleY) | Scale vertically; -1 flips the vertical axis. |
zoom(ratio, showTooltip?, pivot?) | Zoom by a relative ratio: positive enlarges, negative reduces; optionally specify a tooltip and pivot. |
zoomTo(ratio, showTooltip?, pivot?) | Set an absolute zoom ratio, with 1 representing natural size; optionally specify a tooltip and pivot. |
play(fullscreen?) | Start image playback, optionally requesting browser fullscreen. |
stop() | End image playback. |
full() | Switch an inline viewer into modal mode. |
exit() | Return an expanded inline viewer from modal mode to inline display. |
showTooltip() | Display the current zoom percentage when tooltip is enabled; renamed from native tooltip() to avoid the prop collision. |
toggle() | Switch between the current and natural image sizes. |
reset() | Restore the image’s initial state. |
update() | Refresh the gallery after source images are added, removed or reordered. |
destroy() | Dispose of the viewer instance. |
Wait for @viewed before manipulating an image. Except for update() and destroy(), the methods listed above return the Viewer.js instance, or undefined when none exists.
update() and destroy() return nothing. update() creates a missing instance and takes no options—change props to configure the component. destroy() also disconnects observation and clears the component’s instance reference, retaining the source images.
v-viewer-specific methods
| Method | Purpose |
|---|---|
createViewer() | Create an instance if none exists, using current props and image DOM. |
rebuildViewer() | Destroy the current instance and recreate it using current props and image DOM; create one if absent. |
For underlying instance data, read gallery.value.viewer.
Slots
Default slot
Render images and custom layouts in the default slot.
<Viewer :toolbar="false">
<img v-for="src in images" :key="src" :src="src" />
</Viewer>Rendered HTML can also be used as the slot content:
<Viewer>
<article v-html="html" />
</Viewer>Toolbar slots
Use Vue templates, icon components and event bindings to customize the toolbar.
| Slot | Behavior |
|---|---|
toolbar-append | Add content after the toolbar, including after a custom toolbar slot. |
toolbar | Replace all built-in toolbar buttons with custom icons, layout and interactions. |
Both slots receive image (the current preview's HTMLImageElement, or undefined before it is viewed), index (zero-based), and the component's Viewer.js methods, such as zoom, rotate, reset and next. Slot parameters update with the preview state.
toolbar: false hides the entire toolbar, including its slots. Without a replacement slot, the existing toolbar configuration still determines which buttons are present.
Example code
<script setup>
import { ref } from 'vue'
import { Viewer } from 'v-viewer'
const images = [
'/images/photo-01.jpg',
'/images/photo-02.jpg',
'/images/photo-03.jpg',
'/images/photo-04.jpg',
'/images/photo-05.jpg',
]
const inline = ref(true)
const custom = ref(false)
</script>
<template>
<main class="viewer-example toolbar-example">
<div class="controls">
<label><input v-model="inline" type="checkbox" /> Inline mode</label>
<label
><input v-model="custom" type="checkbox" /> Replace the toolbar</label
>
</div>
<div class="toolbar-stage" :class="{ 'is-inline': inline }">
<Viewer :inline="inline" :focus="!inline" :min-height="320">
<div v-show="!inline" class="gallery">
<img
v-for="(src, index) in images"
:key="src"
:src="src"
:alt="'Landscape ' + (index + 1)"
/>
</div>
<template v-if="custom" #toolbar="{ zoom, rotate, reset }">
<button
type="button"
class="viewer-slot-button viewer-custom-button"
title="Zoom in"
aria-label="Zoom in"
@click="zoom(0.1)"
>
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<circle cx="10" cy="10" r="6.5" />
<path d="m15 15 5.5 5.5M7 10h6m-3-3v6" />
</svg>
</button>
<button
type="button"
class="viewer-slot-button viewer-custom-button"
title="Rotate"
aria-label="Rotate"
@click="rotate(90)"
>
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<rect x="4" y="11" width="9" height="9" rx="1.5" />
<path d="M5 7a9 9 0 0 1 15 7m0-5v5h-5" />
</svg>
</button>
<button
type="button"
class="viewer-slot-button viewer-custom-button"
title="Reset"
aria-label="Reset"
@click="reset()"
>
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path
d="M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5M8 12a4 4 0 1 0 1-2.6M8 7v3h3"
/>
</svg>
</button>
</template>
<template #toolbar-append="{ image, index }">
<a
v-if="image"
class="viewer-slot-button"
:class="{ 'viewer-custom-button': custom }"
:href="image.src"
:download="image.alt"
:title="'Download image ' + (index + 1)"
:aria-label="'Download image ' + (index + 1)"
>
<svg
width="16"
height="16"
viewBox="0 0 20 20"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<path d="M10 2v10m-4-4 4 4 4-4M3 12v5h14v-5" />
</svg>
</a>
</template>
</Viewer>
</div>
</main>
</template>Migrating from older versions
<!-- Legacy: supported, but deprecated -->
<Viewer :options="{ toolbar: false, viewed: onViewed }">
<img src="/photo.jpg" />
</Viewer>
<!-- Recommended -->
<Viewer :toolbar="false" @viewed="onViewed">
<img src="/photo.jpg" />
</Viewer>