Viewport
The viewport controls the visible area of your flow diagram, including zoom level, pan position, and user interaction settings. Understanding viewport management is essential for creating intuitive flow experiences.
The viewport transform defines the current view state:
interface ViewportTransform {
x: number // Horizontal pan offset
y: number // Vertical pan offset
zoom: number // Zoom level (1 = 100%)
}
Viewport coordinates use screen space, not flow space. Positive x moves the canvas left, positive y moves it up.
Getting Viewport State
Access the current viewport:
useVueFlow
Viewport Events
import { useVueFlow } from '@vue-flow/core'
const { viewport, getViewport } = useVueFlow()
// Reactive viewport ref
console.log(viewport.value) // { x: 0, y: 0, zoom: 1 }
// Get current transform
const transform = getViewport()
console.log(transform) // { x: 0, y: 0, zoom: 1 }
<script setup>
import { useVueFlow } from '@vue-flow/core'
const { onViewportChange } = useVueFlow()
onViewportChange((viewport) => {
console.log('Viewport changed:', viewport)
})
</script>
Setting Viewport
Programmatically control the viewport:
import { useVueFlow } from '@vue-flow/core'
const { setViewport, viewport } = useVueFlow()
// Set viewport instantly
await setViewport({ x: 100, y: 50, zoom: 1.5 })
// Set with animation
await setViewport(
{ x: 100, y: 50, zoom: 1.5 },
{
duration: 300,
ease: (t) => t * t // Easing function
}
)
// Or mutate directly (no animation)
viewport.value = { x: 0, y: 0, zoom: 1 }
Animation duration in milliseconds
Easing function (receives value 0-1, returns 0-1)
interpolate
'smooth' | 'linear'
default:"'smooth'"
Interpolation method for smooth transitions
Zoom Controls
Zoom In/Out
import { useVueFlow } from '@vue-flow/core'
const { zoomIn, zoomOut } = useVueFlow()
// Zoom in by 20%
await zoomIn()
// Zoom out by 20%
await zoomOut()
// With animation
await zoomIn({ duration: 300 })
Zoom to Level
const { zoomTo } = useVueFlow()
// Set specific zoom level
await zoomTo(1.5) // 150%
await zoomTo(0.5) // 50%
// With animation
await zoomTo(2, {
duration: 500,
interpolate: 'linear'
})
Zoom Constraints
Control minimum and maximum zoom levels:
<script setup>
import { useVueFlow } from '@vue-flow/core'
const { setMinZoom, setMaxZoom, minZoom, maxZoom } = useVueFlow()
// Set zoom bounds
setMinZoom(0.5) // 50%
setMaxZoom(3) // 300%
// Access current bounds
console.log(minZoom.value) // 0.5
console.log(maxZoom.value) // 3
</script>
<template>
<!-- Or set via props -->
<VueFlow :min-zoom="0.5" :max-zoom="3" />
</template>
Fit View
Automatically frame all nodes in the viewport:
import { useVueFlow } from '@vue-flow/core'
const { fitView } = useVueFlow()
// Fit all nodes
await fitView()
// With options
await fitView({
padding: 0.2, // 20% padding around nodes
includeHiddenNodes: false, // Exclude hidden nodes
minZoom: 0.5, // Override min zoom
maxZoom: 1.5, // Override max zoom
duration: 300, // Animation duration
nodes: ['1', '2'] // Only fit specific nodes
})
padding
number | Padding
default:"0.1"
Padding around nodes. Use number (0.1 = 10%) or object:padding: {
top: 50, // or '10%'
right: 50,
bottom: 50,
left: 50,
x: 50, // Shorthand for left + right
y: 50 // Shorthand for top + bottom
}
Include hidden nodes in fit calculation
Array of node IDs to fit. If not provided, fits all nodes.
Fit View on Init
<template>
<!-- Automatically fit view when component mounts -->
<VueFlow :fit-view-on-init="true" />
</template>
Center Viewport
Center the viewport on specific coordinates:
import { useVueFlow } from '@vue-flow/core'
const { setCenter } = useVueFlow()
// Center on coordinates
await setCenter(100, 200)
// Center with zoom
await setCenter(100, 200, { zoom: 1.5 })
// With animation
await setCenter(100, 200, {
zoom: 2,
duration: 500
})
Fit Bounds
Fit viewport to a specific rectangular area:
import { useVueFlow } from '@vue-flow/core'
import type { Rect } from '@vue-flow/core'
const { fitBounds } = useVueFlow()
const bounds: Rect = {
x: 0,
y: 0,
width: 500,
height: 300
}
await fitBounds(bounds, {
padding: 0.1,
duration: 300
})
Panning
Pan by Delta
import { useVueFlow } from '@vue-flow/core'
const { panBy } = useVueFlow()
// Pan viewport
panBy({ x: 100, y: 50 }) // Returns boolean indicating if transform changed
Pan on Drag
Control panning behavior when dragging the canvas:
<template>
<!-- Enable pan on drag (default: true) -->
<VueFlow :pan-on-drag="true" />
<!-- Disable pan on drag -->
<VueFlow :pan-on-drag="false" />
<!-- Pan only with specific mouse buttons -->
<VueFlow :pan-on-drag="[1, 2]" /> <!-- Middle and right click -->
</template>
Use scroll wheel for panning instead of zooming:
<script setup>
import { PanOnScrollMode } from '@vue-flow/core'
</script>
<template>
<!-- Pan in all directions -->
<VueFlow
:pan-on-scroll="true"
:zoom-on-scroll="false"
:pan-on-scroll-mode="PanOnScrollMode.Free"
:pan-on-scroll-speed="0.5"
/>
<!-- Pan vertically only -->
<VueFlow
:pan-on-scroll="true"
:pan-on-scroll-mode="PanOnScrollMode.Vertical"
/>
<!-- Pan horizontally only -->
<VueFlow
:pan-on-scroll="true"
:pan-on-scroll-mode="PanOnScrollMode.Horizontal"
/>
</template>
Coordinate Conversion
Convert between screen and flow coordinates:
import { useVueFlow } from '@vue-flow/core'
const {
project, // Alias for screenToFlowCoordinate
screenToFlowCoordinate, // Screen -> Flow
flowToScreenCoordinate // Flow -> Screen
} = useVueFlow()
// Convert mouse position to flow coordinates
function handleClick(event: MouseEvent) {
const flowPosition = screenToFlowCoordinate({
x: event.clientX,
y: event.clientY
})
console.log('Flow position:', flowPosition)
}
// Convert node position to screen coordinates
const node = { position: { x: 100, y: 100 } }
const screenPosition = flowToScreenCoordinate(node.position)
console.log('Screen position:', screenPosition)
Translate Extent
Limit how far users can pan the viewport:
import { useVueFlow } from '@vue-flow/core'
import type { CoordinateExtent } from '@vue-flow/core'
const { setTranslateExtent } = useVueFlow()
// Define boundaries
const extent: CoordinateExtent = [
[0, 0], // Top-left corner
[1000, 1000] // Bottom-right corner
]
setTranslateExtent(extent)
<template>
<!-- Or via props -->
<VueFlow :translate-extent="[[0, 0], [1000, 1000]]" />
</template>
Viewport Interaction Settings
<template>
<VueFlow
<!-- Zoom Controls -->
:zoom-on-scroll="true"
:zoom-on-pinch="true"
:zoom-on-double-click="true"
:min-zoom="0.5"
:max-zoom="2"
<!-- Pan Controls -->
:pan-on-drag="true"
:pan-on-scroll="false"
:pan-on-scroll-speed="0.5"
:pan-on-scroll-mode="PanOnScrollMode.Free"
<!-- Interaction Keys -->
:zoom-activation-key-code="'Control'"
:pan-activation-key-code="'Space'"
<!-- Prevent page scroll when interacting -->
:prevent-scrolling="true"
<!-- Default viewport on mount -->
:default-viewport="{ x: 100, y: 100, zoom: 1.5 }"
/>
</template>
Viewport Events
<script setup>
import { useVueFlow } from '@vue-flow/core'
const {
onViewportChange,
onViewportChangeStart,
onViewportChangeEnd,
onMoveStart,
onMove,
onMoveEnd
} = useVueFlow()
// Viewport transform changes
onViewportChange((viewport) => {
console.log('Viewport:', viewport)
})
onViewportChangeStart((viewport) => {
console.log('Viewport change started')
})
onViewportChangeEnd((viewport) => {
console.log('Viewport change ended')
})
// User movement (dragging, zooming)
onMoveStart((event) => {
console.log('User started moving viewport')
})
onMove((event) => {
console.log('User is moving viewport')
})
onMoveEnd((event) => {
console.log('User stopped moving viewport')
})
</script>
Save and Restore Viewport
import { useVueFlow } from '@vue-flow/core'
const { toObject, fromObject, getViewport } = useVueFlow()
// Save flow state including viewport
function saveFlow() {
const flowObject = toObject()
localStorage.setItem('flow', JSON.stringify(flowObject))
// flowObject contains: { nodes, edges, viewport }
}
// Restore flow state
async function restoreFlow() {
const flowData = JSON.parse(localStorage.getItem('flow')!)
await fromObject(flowData)
// Automatically restores viewport, nodes, and edges
}
// Or save/restore viewport only
function saveViewport() {
const viewport = getViewport()
localStorage.setItem('viewport', JSON.stringify(viewport))
}
async function restoreViewport() {
const viewport = JSON.parse(localStorage.getItem('viewport')!)
await setViewport(viewport)
}
Practical Examples
Reset Viewport
import { useVueFlow } from '@vue-flow/core'
const { setViewport } = useVueFlow()
function resetViewport() {
setViewport({ x: 0, y: 0, zoom: 1 }, { duration: 300 })
}
Focus on Node
import { useVueFlow } from '@vue-flow/core'
const { setCenter, findNode } = useVueFlow()
function focusNode(nodeId: string) {
const node = findNode(nodeId)
if (node) {
setCenter(
node.position.x + (node.dimensions.width / 2),
node.position.y + (node.dimensions.height / 2),
{ zoom: 1.5, duration: 500 }
)
}
}
Minimap Integration
<script setup>
import { VueFlow } from '@vue-flow/core'
import { MiniMap } from '@vue-flow/minimap'
import '@vue-flow/minimap/dist/style.css'
</script>
<template>
<VueFlow>
<MiniMap />
</VueFlow>
</template>
The MiniMap automatically syncs with viewport changes and allows clicking to navigate.
See Also
- State Management - Access viewport in global state
- Nodes - Position nodes in flow space
- Edges - Understand coordinate systems for edges