What is VueUse?
VueUse is an open-source library that offers a comprehensive set of composable functions designed specifically for Vue 3 and the Composition API. Each composable encapsulates a specific piece of functionality--from simple ref manipulations to complex browser API integrations--making it easy to add sophisticated features to your applications with minimal code.
What sets VueUse apart is its commitment to three core principles: being fully tree-shakeable so you only include what you need, providing excellent TypeScript support with full type definitions, and maintaining SSR (Server-Side Rendering) compatibility for frameworks like Nuxt.js. These design decisions mean VueUse integrates seamlessly into both small projects and large-scale applications without adding unnecessary bundle size or compatibility issues.
For teams working with Nuxt.js, VueUse becomes even more valuable through the official @vueuse/nuxt module, which provides automatic importing of all composables. This eliminates import statements cluttering your codebase while ensuring you always use the most up-to-date versions of the utilities you need.
When building modern Vue applications, leveraging battle-tested composables like VueUse can significantly reduce development time while improving code quality and maintainability across your projects.
Leverage battle-tested implementations
200+ Composable Functions
Comprehensive utilities for state, elements, browsers, sensors, and more
Fully Tree-Shakeable
Only bundle the composables you actually use
TypeScript Support
Full type definitions with excellent inference
SSR Compatible
Works seamlessly with Nuxt.js server-side rendering
Getting Started with VueUse
Installation
For Vue 3 projects:
npm install @vueuse/core
For Nuxt.js projects:
npm install @vueuse/nuxt @vueuse/core
Nuxt Configuration
Add the module to your nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@vueuse/nuxt']
})
With this configuration, all VueUse composables become automatically available throughout your application without explicit imports, reducing boilerplate while maintaining full TypeScript support.
Core Composables: State Management
VueUse provides several composables for managing reactive state, going beyond Vue's built-in ref and reactive to handle persistent and synchronized state across components or browser sessions.
useStorage
The useStorage composable automatically synchronizes reactive state with browser localStorage or sessionStorage. Unlike manual storage implementations that require serialization/deserialization and event listeners for cross-tab synchronization, useStorage handles all these concerns automatically:
import { useStorage } from '@vueuse/core'
// Persist state to localStorage automatically
const settings = useStorage('user-settings', {
theme: 'light',
fontSize: 16,
notifications: true
})
// Changes automatically persist and sync across tabs
settings.value.theme = 'dark'
The composable supports JSON serialization, handles browser storage limits gracefully, and automatically synchronizes state across multiple browser tabs using the storage event. For sensitive data or when localStorage isn't available, it seamlessly falls back to in-memory state.
useGlobalState
Another powerful state management composable is createGlobalState, which provides a simple global state management solution without external libraries. This is particularly useful for sharing state across distant components without prop drilling:
import { createGlobalState } from '@vueuse/core'
// Create a global state that can be used anywhere
const useGlobalCounter = createGlobalState(() => ref(0))
// In any component
const counter = useGlobalCounter()
For applications requiring more sophisticated state management, VueUse integrates well with Pinia or Vuex while providing utility composables that enhance these libraries' capabilities.
Core Composables: Element Interactions
Interacting with DOM elements is a common requirement in Vue applications, and VueUse provides composables that make these interactions intuitive and reactive.
useElementSize
The useElementSize composable tracks the dimensions of any DOM element, updating reactively as the element resizes. This is invaluable for responsive designs where components need to adapt their layout based on available space:
import { useElementSize } from '@vueuse/core'
const cardRef = ref<HTMLElement | null>(null)
const { width, height } = useElementSize(cardRef)
watch(width, (newWidth) => {
// Adapt layout based on available width
columns.value = newWidth > 600 ? 3 : 1
})
useDraggable
The useDraggable composable enables drag-and-drop functionality with minimal configuration, returning position coordinates and drag state that integrate naturally with Vue's reactivity system:
import { useDraggable } from '@vueuse/core'
const elementRef = ref<HTMLElement | null>(null)
const { x, y, isDragging } = useDraggable(elementRef, {
initialValue: { x: 100, y: 100 }
})
onClickOutside
For implementing modals, dropdowns, or tooltips that should close when users click outside them, onClickOutside provides a clean solution:
import { onClickOutside } from '@vueuse/core'
const modalRef = ref<HTMLElement | null>(null)
onClickOutside(modalRef, () => {
// Close modal when clicking outside
emit('close')
})
Implementing this feature manually requires understanding the DOM event model, managing event listener cleanup to prevent memory leaks, and handling various edge cases like iframe interactions. VueUse's onClickOutside encapsulates all this complexity.
Browser and Window Composables
VueUse includes extensive support for browser APIs, making it easy to reactively access browser state and respond to user environment changes.
Dark Mode with useDark
import { useDark, useToggle } from '@vueuse/core'
// Automatically detect and respect system preference
const isDark = useDark()
const toggleDark = useToggle(isDark)
// Toggle in your template
<button @click="toggleDark()">
{{ isDark ? 'Switch to Light Mode' : 'Switch to Dark Mode' }}
</button>
useMediaQuery
The useMediaQuery composable enables reactive CSS media query handling:
import { useMediaQuery } from '@vueuse/core'
const isLargeScreen = useMediaQuery('(min-width: 1024px)')
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)')
Scroll Tracking
For tracking scroll position and implementing scroll-dependent features, useWindowScroll and useScroll provide reactive access to scroll coordinates:
import { useWindowScroll, useScroll } from '@vueuse/core'
const { y: scrollY } = useWindowScroll()
const elementRef = ref<HTMLElement | null>(null)
const { y: elementScrollY } = useScroll(elementRef)
useClipboard
The useClipboard composable abstracts the Clipboard API with graceful fallbacks for older browsers:
import { useClipboard } from '@vueuse/core'
const { copy, copied } = useClipboard()
const copyToClipboard = async (text: string) => {
await copy(text)
// Use 'copied' ref to show feedback
}
Sensor and Event Composables
VueUse provides several composables for tracking user input, enabling sophisticated interactive features without manual event management.
Mouse Tracking
import { useMouse, useMouseInElement } from '@vueuse/core'
// Track mouse position relative to viewport
const { x, y } = useMouse()
// Track mouse position relative to an element
const elementRef = ref<HTMLElement | null>(null)
const { x: elementX, y: elementY, isOutside } = useMouseInElement(elementRef)
This composable is particularly useful for implementing custom cursors, parallax effects, and hover-dependent features that need precise mouse position information.
useSwipe
The useSwipe composable detects swipe gestures on touch devices, supporting both touch and mouse drag events:
import { useSwipe } from '@vueuse/core'
const elementRef = ref<HTMLElement | null>(null)
const { isSwiping, direction, lengthX, lengthY } = useSwipe(elementRef, {
onSwipeEnd: () => {
if (direction.value === 'left') {
navigateToPrevious()
} else if (direction.value === 'right') {
navigateToNext()
}
}
})
useMagicKeys
The useMagicKeys composable provides a powerful interface for tracking keyboard shortcuts and key combinations:
import { useMagicKeys, whenever } from '@vueuse/core'
const { Ctrl_K, Shift_Escape, a, b } = useMagicKeys()
// Watch for specific key combinations
whenever(Ctrl_K.value, () => {
openCommandPalette()
})
// Complex combination: Ctrl+Shift+A or Ctrl+B
whenever(
(Ctrl_K.value && Shift_Escape.value) || (Ctrl_K.value && b.value),
() => {
// Handle the combination
}
)
Integrating VueUse with Nuxt.js
Automatic Imports
The @vueuse/nuxt module transforms how you work with composables in Nuxt applications by providing automatic imports. This means you can use any VueUse composable without importing it, and Nuxt's build process will automatically include only what you actually use:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@vueuse/nuxt'],
vueuse: {
// Optional configuration
autoImports: true,
imports: ['useMouse', 'useDark', 'useStorage']
}
})
In your Vue components and Nuxt pages, simply use composables directly:
<script setup lang="ts">
// These are auto-imported by @vueuse/nuxt
const { x, y } = useMouse()
const isDark = useDark()
const counter = useStorage('counter', 0)
</script>
SSR-Safe Composables
The integration includes SSR-safe versions of browser APIs, ensuring your Nuxt applications work correctly during server-side rendering without hydration mismatches:
import { useWindowSize } from '#imports'
// Safe to use in SSR - returns null or default values during SSR
const { width, height } = useWindowSize()
For Nuxt projects using server-side rendering, the automatic import system works with tree-shaking to ensure you never bundle unused composables, even when you use the auto-import feature extensively.
Performance Optimization with VueUse
Tree-Shaking
One of VueUse's most important features is its tree-shakeable architecture. Each composable is exported as a separate function, so bundlers like Vite or Webpack can eliminate unused code from your final bundle:
// Only useMouse ends up in your bundle
import { useMouse } from '@vueuse/core'
This means adding VueUse to your project doesn't automatically increase bundle size by 200+ composables--only the composables you actually import and use contribute to the final bundle. For high-performance web applications, this optimization is essential for maintaining fast load times and smooth user experiences.
Lazy Loading
VueUse composables can be combined with Vue's async components and Nuxt's lazy loading for optimal performance:
import { defineAsyncComponent } from 'vue'
import { useIntersectionObserver } from '@vueuse/core'
const HeavyComponent = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
)
const target = ref(null)
const isVisible = useIntersectionObserver(target, ([entry]) => {
return entry.isIntersecting
})
// Only load when target becomes visible
<HeavyComponent v-if="isVisible.value" ref="target" />
Best Practices
- Let VueUse handle automatic cleanup on component unmount
- Use TypeScript for full type inference
- Combine composables for sophisticated patterns
- Leverage auto-imports in Nuxt for cleaner code
VueUse composables automatically handle cleanup when components unmount, preventing memory leaks in your applications.
Building Common Features with VueUse
Infinite Scroll
Infinite scroll is a common requirement that VueUse makes straightforward:
import { useInfiniteScroll } from '@vueuse/core'
const el = ref<HTMLElement | null>(null)
const data = ref<Array<any>>([])
const loadMore = async () => {
const newItems = await fetchItems(data.value.length)
data.value.push(...newItems)
}
useInfiniteScroll(el, loadMore, { distance: 10 })
Toast Notifications
VueUse can power a reusable toast notification system:
import { useArrayFindLast } from '@vueuse/core'
const toasts = ref<Array<{id: number, message: string, type: string}>>([])
const addToast = (message: string, type = 'info') => {
const id = Date.now()
toasts.value.push({ id, message, type })
// Auto-remove after 3 seconds
setTimeout(() => {
toasts.value = toasts.value.filter(t => t.id !== id)
}, 3000)
}
// Find most recent toast of a specific type
const latestError = useArrayFindLast(toasts, t => t.type === 'error')
Form Validation Feedback
Combine VueUse composables for responsive form validation:
import { useDebounceFn, useStorage } from '@vueuse/core'
const email = ref('')
const emailError = ref('')
const checkEmailAvailability = useDebounceFn(async (email) => {
const response = await fetch(`/api/check-email?email=${email}`)
const { available } = await response.json()
emailError.value = available ? '' : 'Email already registered'
}, 500)
watch(email, (newEmail) => {
if (newEmail.includes('@')) {
checkEmailAvailability(newEmail)
}
})
Network Status
The useOnline composable detects network connectivity changes:
import { useOnline } from '@vueuse/core'
const isOnline = useOnline()
// Show offline indicator when disconnected
watch(isOnline, (online) => {
if (!online) {
showOfflineNotification()
}
})
Frequently Asked Questions
Sources
- VueUse Official Site - Primary documentation with 200+ composables for Vue 3
- VueUse GitHub Repository - Open source codebase with 16k+ stars
- Mastering Nuxt: 15 Must-Have Nuxt Modules - Nuxt integration details
- Nuxt Documentation - Composables - Nuxt composables directory structure