Add zoom and pan capabilities to images. Builds on the base Image component with click-to-zoom, cursor-following zoom, touch gestures (pinch, pan, double-tap), and optional controls.
<script setup lang="ts">
import { ImageFallback } from "@/components/image";
import { ImageViewerContainer, ImageViewerProvider, ImageViewerSource, ImageViewerZoomInControl, ImageViewerZoomOutControl } from "@/components/image-viewer";
</script>
<template>
<ImageViewerProvider :scale="1" :max-scale="3" :step="2" :reset-on-click-outside="false">
<ImageViewerContainer class="relative">
<ImageViewerSource class="w-96 aspect-video" src="https://picsum.photos/id/229/600/400" alt="Zoomable image" />
<ImageFallback as-child>
<div class="w-96 aspect-video p-2 text-center text-muted-foreground bg-muted">
Loading...
</div>
</ImageFallback>
<div class="hidden md:flex p-1 bg-background/75 rounded-lg absolute bottom-2 right-2 pointer-events-auto backdrop-blur-lg">
<ImageViewerZoomInControl variant="ghost" />
<ImageViewerZoomOutControl variant="ghost" />
</div>
</ImageViewerContainer>
</ImageViewerProvider>
</template>
Features
- Click to zoom — Zoom in/out by clicking on the image (desktop)
- Cursor following — Zoom centers on cursor position for intuitive navigation
- Touch support — Pinch to zoom, pan when zoomed, double-tap to toggle max zoom
- Zoom controls — Buttons, slider, and minimap for precise control
- Composable — Use with Image component for loading states
Installation
Install from the Vuzeno registry. Requires the Image component:
bunx --bun shadcn-vue@latest add https://vuzeno.com/r/image-viewer.json
Examples
Basic
Wrap your image with ImageViewerProvider and ImageViewerContainer, then use ImageViewerSource:
<template>
<ImageViewerProvider :scale="1" :max-scale="6">
<ImageViewerContainer>
<ImageViewerSource src="..." alt="..." />
<ImageFallback>
<div class="p-2 text-center text-muted-foreground bg-muted">Loading...</div>
</ImageFallback>
</ImageViewerContainer>
</ImageViewerProvider>
</template>
With Controls
<script setup lang="ts">
import { ImageFallback } from "@/components/image";
import { ImageViewerContainer, ImageViewerProvider, ImageViewerSource, ImageViewerZoomInControl, ImageViewerZoomMap, ImageViewerZoomOutControl, ImageViewerZoomSlider } from "@/components/image-viewer";
</script>
<template>
<ImageViewerProvider class="flex flex-col gap-4" :scale="1" :max-scale="6" :step="2" :reset-on-click-outside="false">
<div class="flex gap-2">
<div class="hidden md:flex flex-col gap-4"></div>
<ImageViewerContainer class="relative">
<ImageViewerSource src="https://picsum.photos/id/229/600/400" alt="Zoomable image" />
<ImageFallback>
<div class="p-2 text-center text-muted-foreground bg-muted">
Loading...
</div>
</ImageFallback>
<div class="hidden md:flex p-1 bg-background/75 rounded-lg absolute bottom-2 right-2 pointer-events-auto backdrop-blur-lg">
<ImageViewerZoomInControl variant="ghost" />
<ImageViewerZoomOutControl variant="ghost" />
</div>
</ImageViewerContainer>
<ImageViewerZoomSlider orientation="vertical" class="data-[orientation=vertical]:h-auto flex-none" />
</div>
<ImageViewerZoomMap />
</ImageViewerProvider>
</template>
Add zoom controls and minimap for full control:
<template>
<ImageViewerProvider :scale="1" :max-scale="6">
<ImageViewerContainer>
<ImageViewerSource src="..." alt="..." />
<div class="absolute bottom-2 right-2 flex gap-2">
<ImageViewerZoomInControl />
<ImageViewerZoomOutControl />
</div>
</ImageViewerContainer>
<ImageViewerZoomSlider orientation="vertical" />
<ImageViewerZoomMap />
</ImageViewerProvider>
</template>
Cursor Following
The followCursor prop controls how the zoomed image is positioned:
| Mode | Behavior |
|---|---|
followCursor: true (default) | The zoom focuses on the cursor position. As you move the mouse, the visible area shifts to keep the cursor point centered. |
followCursor: false | The zoom is centered on the image. The visible area remains fixed at the center regardless of cursor position. |
When using ImageViewerZoomSlider, the component automatically disables cursor following during drag operations to prevent visual conflicts.
Touch Gestures
On touch devices:
- Pinch — Two-finger pinch to zoom in/out
- Pan — Single-finger drag when zoomed to pan the image
- Double-tap — Toggle between max zoom and reset