Overview
The v-resize directive listens to window resize events and calls a handler function. Useful for responsive layouts, canvas resizing, and dynamic calculations based on viewport size.
Import
import { Resize } from 'vuetify/directives'
Registration
Global Registration
import { createApp } from 'vue'
import { Resize } from 'vuetify/directives'
const app = createApp({})
app.directive('resize', Resize)
Local Registration
<script setup>
import { Resize } from 'vuetify/directives'
const vResize = Resize
</script>
Syntax
<!-- Basic usage -->
<div v-resize="handler"></div>
<!-- With modifiers -->
<div v-resize.quiet="handler"></div>
<div v-resize.active="handler"></div>
Value
Handler function called on window resize
Modifiers
Don’t call handler immediately on mount, only on actual resize events
Use active event listener (not passive). Allows preventDefault() in handler.
Usage Examples
Basic Window Resize
<script setup>
import { ref } from 'vue'
const windowSize = ref({
width: window.innerWidth,
height: window.innerHeight
})
function onResize() {
windowSize.value = {
width: window.innerWidth,
height: window.innerHeight
}
}
</script>
<template>
<div v-resize="onResize">
<p>Width: {{ windowSize.width }}px</p>
<p>Height: {{ windowSize.height }}px</p>
</div>
</template>
Responsive Canvas
<script setup>
import { ref, onMounted } from 'vue'
const canvas = ref<HTMLCanvasElement | null>(null)
const ctx = ref<CanvasRenderingContext2D | null>(null)
onMounted(() => {
if (canvas.value) {
ctx.value = canvas.value.getContext('2d')
resizeCanvas()
}
})
function resizeCanvas() {
if (canvas.value) {
canvas.value.width = window.innerWidth
canvas.value.height = window.innerHeight
draw()
}
}
function draw() {
if (ctx.value && canvas.value) {
ctx.value.clearRect(0, 0, canvas.value.width, canvas.value.height)
// Drawing logic here
}
}
</script>
<template>
<canvas ref="canvas" v-resize="resizeCanvas" />
</template>
Breakpoint Detection
<script setup>
import { ref } from 'vue'
const breakpoint = ref('desktop')
function updateBreakpoint() {
const width = window.innerWidth
if (width < 600) {
breakpoint.value = 'mobile'
} else if (width < 960) {
breakpoint.value = 'tablet'
} else {
breakpoint.value = 'desktop'
}
}
</script>
<template>
<div v-resize="updateBreakpoint">
<p>Current breakpoint: {{ breakpoint }}</p>
<v-container v-if="breakpoint === 'mobile'">
Mobile layout
</v-container>
<v-container v-else-if="breakpoint === 'tablet'">
Tablet layout
</v-container>
<v-container v-else>
Desktop layout
</v-container>
</div>
</template>
Quiet Modifier
<script setup>
import { ref } from 'vue'
const resizeCount = ref(0)
function onResize() {
resizeCount.value++
}
</script>
<template>
<!-- Handler not called on mount, only on actual resize -->
<div v-resize.quiet="onResize">
<p>Resize count: {{ resizeCount }}</p>
<p>Try resizing the window</p>
</div>
</template>
Debounced Resize Handler
<script setup>
import { ref } from 'vue'
const dimensions = ref({ width: 0, height: 0 })
let resizeTimeout: number | null = null
function onResize() {
if (resizeTimeout) {
clearTimeout(resizeTimeout)
}
resizeTimeout = setTimeout(() => {
dimensions.value = {
width: window.innerWidth,
height: window.innerHeight
}
}, 250) as unknown as number
}
</script>
<template>
<div v-resize="onResize">
<p>Dimensions: {{ dimensions.width }} x {{ dimensions.height }}</p>
</div>
</template>
Aspect Ratio Calculator
<script setup>
import { ref, computed } from 'vue'
const width = ref(window.innerWidth)
const height = ref(window.innerHeight)
const aspectRatio = computed(() => {
const gcd = (a: number, b: number): number =>
b === 0 ? a : gcd(b, a % b)
const divisor = gcd(width.value, height.value)
return `${width.value / divisor}:${height.value / divisor}`
})
function updateDimensions() {
width.value = window.innerWidth
height.value = window.innerHeight
}
</script>
<template>
<div v-resize="updateDimensions">
<p>{{ width }} x {{ height }}</p>
<p>Aspect Ratio: {{ aspectRatio }}</p>
</div>
</template>
Chart Resize
<script setup>
import { ref, onMounted } from 'vue'
import type { Chart } from 'chart.js'
const chartRef = ref<HTMLCanvasElement | null>(null)
const chart = ref<Chart | null>(null)
onMounted(() => {
// Initialize chart
if (chartRef.value) {
// chart.value = new Chart(chartRef.value, config)
}
})
function resizeChart() {
if (chart.value) {
chart.value.resize()
}
}
</script>
<template>
<div v-resize.quiet="resizeChart">
<canvas ref="chartRef" />
</div>
</template>
Dynamic Grid Columns
<script setup>
import { ref, computed } from 'vue'
const containerWidth = ref(window.innerWidth)
const columnWidth = 300
const columns = computed(() =>
Math.floor(containerWidth.value / columnWidth)
)
function updateWidth() {
containerWidth.value = window.innerWidth
}
const items = Array.from({ length: 20 }, (_, i) => i)
</script>
<template>
<div v-resize="updateWidth">
<p>Columns: {{ columns }}</p>
<v-row>
<v-col
v-for="item in items"
:key="item"
:cols="12 / columns"
>
Item {{ item }}
</v-col>
</v-row>
</div>
</template>
Orientation Change
<script setup>
import { ref } from 'vue'
const orientation = ref<'portrait' | 'landscape'>('portrait')
function checkOrientation() {
orientation.value = window.innerHeight > window.innerWidth
? 'portrait'
: 'landscape'
}
</script>
<template>
<div v-resize="checkOrientation">
<v-icon v-if="orientation === 'portrait'">
mdi-cellphone
</v-icon>
<v-icon v-else>
mdi-cellphone-rotate
</v-icon>
<p>{{ orientation }}</p>
</div>
</template>
Font Size Scaling
<script setup>
import { ref } from 'vue'
const fontSize = ref(16)
function scaleFontSize() {
const width = window.innerWidth
// Scale font size based on viewport width
fontSize.value = Math.max(12, Math.min(24, width / 50))
}
</script>
<template>
<div v-resize="scaleFontSize" :style="{ fontSize: `${fontSize}px` }">
Responsive text that scales with viewport
</div>
</template>
Event Listener Options
By default, the directive uses passive event listeners for better performance:
// Default (passive)
<div v-resize="handler"></div>
// Active listener (can preventDefault)
<div v-resize.active="handler"></div>
Performance Considerations
Throttling
<script setup>
function throttle(func: Function, wait: number) {
let timeout: number | null = null
let lastRan: number
return function(this: any, ...args: any[]) {
if (!lastRan) {
func.apply(this, args)
lastRan = Date.now()
} else {
if (timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
if (Date.now() - lastRan >= wait) {
func.apply(this, args)
lastRan = Date.now()
}
}, wait - (Date.now() - lastRan)) as unknown as number
}
}
}
const onResize = throttle(() => {
// Expensive operation
}, 100)
</script>
<template>
<div v-resize="onResize">
Content
</div>
</template>
Debouncing
<script setup>
function debounce(func: Function, wait: number) {
let timeout: number | null = null
return function(this: any, ...args: any[]) {
if (timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
func.apply(this, args)
}, wait) as unknown as number
}
}
const onResize = debounce(() => {
// Expensive operation
}, 250)
</script>
<template>
<div v-resize="onResize">
Content
</div>
</template>
Notes
- Listens to the global
window resize event
- Automatically removes event listener on unmount
- Handler is called immediately on mount (unless
.quiet)
- Uses passive listeners by default for better scroll performance
- Multiple instances can be used simultaneously
- Handler does not receive the resize event object
- Safe to use with conditional rendering (v-if)
See Also