VueUse

Collection of Essential Vue Composition Utilities.

Components

OnClickOutside

vue
<script setup lang="ts">
const count = ref(0);
</script>

<template>
 <OnClickOutside
  @trigger="count++"
 >
  <div>
   Click Outside of Me
  </div>
  <div>
   Count: {{ count }}
  </div>
 </OnClickOutside>
</template>

UseActiveElement

Active element is
vue
<template>
 <UseActiveElement v-slot="{ element }">
  Active element is {{ element?.tagName }}
 </UseActiveElement>
</template>

UseBattery

Is Charging: false
vue
<template>
 <UseBattery v-slot="{ charging }">
  Is Charging: {{ charging }}
 </UseBattery>
</template>

UseClipboard

vue
<template>
 <UseClipboard
  v-slot="{ copy, copied }"
  source="copy me"
 >
  <b-button
   color="button"
   @click="copy()"
  >
   {{ copied ? 'Copied' : 'Copy' }}
  </b-button>
 </UseClipboard>
</template>

UseDeviceMotion

vue
<template>
 <ClientOnly>
  <UseDeviceMotion v-slot="{ acceleration }">
   <JsonView :data="acceleration" />
   Acceleration: {{ acceleration.x }}, {{ acceleration.y }}, {{ acceleration.z }}, {{ acceleration.interval }}
  </UseDeviceMotion>
 </ClientOnly>
</template>

UseDeviceOrientation

vue
<template>
 <ClientOnly>
  <UseDeviceOrientation v-slot="{ alpha, beta, gamma }">
   Alpha: {{ alpha }}
   Beta: {{ beta }}
   Gamma: {{ gamma }}
  </UseDeviceOrientation>
 </ClientOnly>
</template>

UseDevicePixelRatio

vue
<template>
 <ClientOnly>
  <UseDevicePixelRatio v-slot="{ pixelRatio }">
   Pixel Ratio: {{ pixelRatio }}
  </UseDevicePixelRatio>
 </ClientOnly>
</template>

UseDeviceList

vue
<template>
 <ClientOnly>
  <UseDevicesList v-slot="{ videoInputs, audioInputs, audioOutputs }">
   <div>Cameras: {{ videoInputs }}</div>
   <div>Microphones: {{ audioInputs }}</div>
   <div>Speakers: {{ audioOutputs }}</div>
  </UseDevicesList>
 </ClientOnly>
</template>

UseDocumentVisibility

vue
<template>
 <ClientOnly>
  <UseDocumentVisibility v-slot="{ visibility }">
   Document Visibility: {{ visibility }}
  </UseDocumentVisibility>
 </ClientOnly>
</template>

UseDocumentVisibility

Fixed
Drag me! I am at 100, 100
vue
<script setup lang="ts">
const fixed = ref(false);
const style = computed(() => fixed.value ? 'position: fixed' : '');
</script>

<template>
 <b-input
  v-model="fixed"
  type="checkbox"
 />
 Fixed
 <UseDraggable
  v-slot="{ x, y }"
  :initial-value="{ x: 100, y: 100 }"
  :style="style"
 >
  Drag me! I am at {{ x }}, {{ y }}
 </UseDraggable>
</template>

UseElementBounding

{
 "height": 0,
 "bottom": 0,
 "left": 0,
 "right": 0,
 "top": 0,
 "width": 0,
 "x": 0,
 "y": 0
}
  
vue
<template>
 <UseElementBounding
  v-slot="eb"
  style="display: inline-block; border: black solid 1px; padding:10px;"
 >
  <JsonView :data="eb" />
  <textarea
   readonly
   style="resize: both; min-width: 300px;"
  />
 </UseElementBounding>
</template>

UseElementSize

Width: Height:
vue
<template>
 <UseElementSize v-slot="{ width, height }">
  Width: {{ width }} Height: {{ height }}
 </UseElementSize>
</template>

UseElementVisibility

Is Visible: false
vue
<template>
 <UseElementVisibility v-slot="{ isVisible }">
  Is Visible: {{ isVisible }}
 </UseElementVisibility>
</template>

UseEyeDropper

vue
<template>
 <ClientOnly>
  <UseEyeDropper
   v-slot="{ isSupported, sRGBHex, open }"
  >
   <b-button
    color="primary"
    :disabled="!isSupported"
    @click="open"
   >
    Eye Dropper
   </b-button>
   <div>
    sRGBHex: {{ sRGBHex }}
   </div>
  </UseEyeDropper>
 </ClientOnly>
</template>

UseFullscreen

vue
<template>
 <UseFullscreen v-slot="{ toggle }">
  <video
   src="/pexels/movie/3209211-uhd_3840_2160_25fps.mp4"
   class="image-fluid w-100"
   controls
  />
  <b-button
   color="primary"
   @click="toggle"
  >
   Go Fullscreen
  </b-button>
 </UseFullscreen>
</template>

UseGeolocation

Latitude: Infinity Longitude: Infinity
vue
<template>
 <UseGeolocation v-slot="{ coords: { latitude, longitude } }">
  Latitude: {{ latitude }}
  Longitude: {{ longitude }}
 </UseGeolocation>
</template>

UseIdle

Is Idle: false
vue
<template>
 <UseIdle
  v-slot="{ idle }"
  :timeout="5 * 60 * 1000"
 >
  Is Idle: {{ idle }}
 </UseIdle>
</template>

UseMouseInElement

x: 0 y: 0 Is Outside: true
vue
<template>
 <UseMouseInElement v-slot="{ elementX, elementY, isOutside }">
  x: {{ elementX }}
  y: {{ elementY }}
  Is Outside: {{ isOutside }}
 </UseMouseInElement>
</template>

UseMousePressed

Is Pressed: false
vue
<template>
 <UseMousePressed v-slot="{ pressed }">
  Is Pressed: {{ pressed }}
 </UseMousePressed>
</template>

UseNetwork

vue
<template>
 <ClientOnly>
  <UseNetwork v-slot="networkStatus">
   <JsonView :data="networkStatus" />
  </UseNetwork>
 </ClientOnly>
</template>

UseNow

vue
<template>
  <ClientOnly>
  <UseNow v-slot="{ now, pause, resume }">
   {{ now }}
   <b-button
    color="primary"
    @click="pause()"
   >
    Pause
   </b-button>
   <b-button
    color="primary"
    @click="resume()"
   >
    Resume
   </b-button>
  </UseNow>
 </ClientOnly>

</template>

UseObjectUrl

vue
<template>
  <ClientOnly>
  <UseNow v-slot="{ now, pause, resume }">
   {{ now }}
   <b-button
    color="primary"
    @click="pause()"
   >
    Pause
   </b-button>
   <b-button
    color="primary"
    @click="resume()"
   >
    Resume
   </b-button>
  </UseNow>
 </ClientOnly>

</template>

UseOffsetPagination

total:
80
pageCount:
8
currentPageSize:
10
currentPage:
1
isFirstPage:
true
isLastPage:
false
idname
vue
<script setup lang="ts">
interface User {
 id: number;
 name: string;
}
const database = ref([]) as Ref<User[]>;

for (let i = 0; i < 80; i++)
 database.value.push({ id: i, name: `user ${i}` });

function fetch(page: number, pageSize: number) {
 return new Promise<User[]>((resolve, reject) => {
  const start = (page - 1) * pageSize;
  const end = start + pageSize;
  setTimeout(() => {
   resolve(database.value.slice(start, end));
  }, 100);
 });
}

const data: Ref<User[]> = ref([]);

const page = ref(1);
const pageSize = ref(10);

fetchData({
 currentPage: page.value,
 currentPageSize: pageSize.value,
});

function fetchData({ currentPage, currentPageSize }: { currentPage: number; currentPageSize: number }) {
 fetch(currentPage, currentPageSize).then((responseData) => {
  data.value = responseData;
 });
}
</script>

<template>
 <UseOffsetPagination
  v-slot="{
   currentPage,
   currentPageSize,
   next,
   prev,
   pageCount,
   isFirstPage,
   isLastPage,
  }"
  :total="database.length"
  @page-change="fetchData"
  @page-size-change="fetchData"
 >
  <Grid
   :columns="2"
   display="inline-grid"
  >
   <div opacity="50">
    total:
   </div>
   <div>{{ database.length }}</div>
   <div opacity="50">
    pageCount:
   </div>
   <div>{{ pageCount }}</div>
   <div opacity="50">
    currentPageSize:
   </div>
   <div>{{ currentPageSize }}</div>
   <div opacity="50">
    currentPage:
   </div>
   <div>{{ currentPage }}</div>
   <div opacity="50">
    isFirstPage:
   </div>
   <div>{{ isFirstPage }}</div>
   <div opacity="50">
    isLastPage:
   </div>
   <div>{{ isLastPage }}</div>
  </Grid>
  <div>
   <b-button
    :disabled="isFirstPage"
    color="outline-primary"
    @click="prev"
   >
    prev
   </b-button>
   <b-button
    :disabled="isLastPage"
    color="outline-primary"
    @click="next"
   >
    next
   </b-button>
  </div>
 </UseOffsetPagination>
 <b-table
  small
  striped
 >
  <thead>
   <tr>
    <td>id</td>
    <td>name</td>
   </tr>
  </thead>
  <tbody>
   <tr
    v-for="d in data"
    :key="d.id"
   >
    <td>{{ d.id }}</td>
    <td>{{ d.name }}</td>
   </tr>
  </tbody>
 </b-table>
</template>

UseOnline

vue
<template>
 <ClientOnly>
  <UseOnline v-slot="{ isOnline }">
   Is Online: {{ isOnline }}
  </UseOnline>
 </ClientOnly>
</template>

UsePageLeave

Has Left Page: false
vue
<template>
 <UsePageLeave v-slot="{ isLeft }">
  Has Left Page: {{ isLeft }}
 </UsePageLeave>
</template>

UsePointer

vue
<template>
 <ClientOnly>
  <UsePointer v-slot="pointer">
   <JsonView :data="pointer" />
  </UsePointer>
 </ClientOnly>
</template>

UsePointerLock

vue
<template>
 <ClientOnly>
  <UsePointerLock v-slot="pointerLock">
   <JsonView :data="pointerLock" />
  </UsePointerLock>
 </ClientOnly>
</template>

UseTimeAgo

vue
<template>
 <ClientOnly>
  <UseTimeAgo
   v-slot="{ timeAgo }"
   :time="new Date(2021, 0, 1)"
  >
   Time Ago: {{ timeAgo }}
  </UseTimeAgo>
 </ClientOnly>
</template>

UseTimestamp

vue
<template>
 <ClientOnly>
  <UseTimestamp v-slot="{ timestamp }">
   Current Time: {{ timestamp }}
  </UseTimestamp>
 </ClientOnly>
</template>

UseWindowFocus

vue
<template>
 <ClientOnly>
  <UseWindowFocus v-slot="{ focused }">
   Document Focus: {{ focused }}
  </UseWindowFocus>
 </ClientOnly>
</template>

UseWindowSize

Width: Infinity
Height: Infinity
vue
<template>
 <UseWindowSize v-slot="{ width, height }">
  <div>Width: {{ width }}</div>
  <div>Height: {{ height }}</div>
 </UseWindowSize>
</template>

Directives

OnLongPress

Long Pressed: false

vue
<script setup lang="ts">
const longPressedComponent = ref(false);

function onLongPressCallbackDirective(e: PointerEvent) {
 longPressedComponent.value = true;
}
function resetComponent() {
 longPressedComponent.value = false;
}
</script>

<template>
 <p>Long Pressed: {{ longPressedComponent }}</p>

 <b-button
  v-on-long-press.prevent="onLongPressCallbackDirective"
  mergin="l-2"
  color="primary"
  class="ml-2"
 >
  Press long
 </b-button>

 <b-button
  mergin="l-2"
  color="secondary"
  class="ml-2"
  @click="resetComponent"
 >
  Reset
 </b-button>
</template>

OnClickOutside

vue
<script setup lang="ts">
const modal = ref(false);
function closeModal() {
 modal.value = false;
}
</script>

<template>
 <b-button
  color="primary"
  @click="modal = true"
 >
  Open Modal
 </b-button>
 <div
  v-if="modal"
  v-on-click-outside="closeModal"
 >
  Hello World
 </div>
</template>

OnKeyStroke

vue
<script setup lang="ts">
const storoke = ref('');
function onUpdate(e: KeyboardEvent) {
 console.log(e);
 storoke.value = storoke.value + e.key;
}
</script>

<template>
 <div class="mb-3">
  <BFormLabel>
   Type C
  </BFormLabel>
  <BFormInput v-on-key-stroke:c,v="onUpdate" />
 </div>
 <div class="mb-3">
  <BFormLabel>
   With Options
  </BFormLabel>
  <BFormInput v-on-key-stroke:c,v="[onUpdate, { eventName: 'keyup' }]" />
 </div>
 <div>{{ storoke }}</div>
</template>

ElementHover

vue
<script setup lang="ts">
const isHovered = ref(false);
function onHover(hovered: boolean) {
 isHovered.value = hovered;
}
</script>

<template>
 <b-button color="primary" v-element-hover="[onHover, { delayEnter: 1000 }]">
  <span>{{ isHovered ? 'Thank you!' : 'Hover me' }}</span>
 </b-button>
</template>

ElementSize

0 0
vue
<script setup lang="ts">
const width1 = ref(0);
const height1 = ref(0);
function onResize({ width, height }: { width: number; height: number }) {
 width1.value = width;
 height1.value = height;
}
</script>

<template>
 <textarea v-element-size="onResize" />
 <!-- with options -->
 <textarea v-element-size="[onResize, { width: 100, height: 100 }, { box: 'content-box' }]" />
 <div>{{ width1 }}  {{ height1 }}</div>
</template>

ElementVisibility

outside
outside
vue
<script setup>
const target = ref(null);
const isVisible = ref(false);

function onElementVisibility(state) {
 isVisible.value = state;
}
</script>

<template>
 <div v-element-visibility="onElementVisibility">
  {{ isVisible ? 'inside' : 'outside' }}
 </div>

 <!-- with options -->
 <div ref="target">
  <div v-element-visibility="[onElementVisibility, { scrollTarget: target }]">
   {{ isVisible ? 'inside' : 'outside' }}
  </div>
 </div>
</template>

InfiniteScroll

1
2
3
4
5
6
1
2
3
4
5
6
vue
<script setup lang="ts">
const data = ref([1, 2, 3, 4, 5, 6]);

function onLoadMore() {
 const length = data.value.length + 1;
 data.value.push(...Array.from({ length: 5 }, (_, i) => length + i));
}
</script>

<template>
 <b-div
  v-infinite-scroll="onLoadMore"
  overflow="auto"
  class="un-h-200px"
 >
  <div
   v-for="item in data"
   :key="item"
  >
   {{ item }}
  </div>
 </b-div>

 <!-- with options -->
 <b-div
  v-infinite-scroll="[onLoadMore, { distance: 10 }]"
  overflow="auto"
  class="un-h-200px"
 >
  <div
   v-for="item in data"
   :key="item"
  >
   {{ item }}
  </div>
 </b-div>
</template>

IntersectionObserver

Scroll me down!

Hello world!

Scroll me down!

Hello world!

false
vue
<script setup lang="ts">
const root = ref(null);

const isVisible = ref(false);

function onIntersectionObserver([{ isIntersecting }]: IntersectionObserverEntry[]) {
 isVisible.value = isIntersecting;
}
</script>

<template>
 <b-div
  overflow="auto"
  class="un-h-200px"
 >
  <div>
   <p class="un-h-200px">
    Scroll me down!
   </p>
   <div
    v-intersection-observer="onIntersectionObserver"
    class="un-h-200px"
   >
    <p>Hello world!</p>
   </div>
  </div>

  <!-- with options -->
  <div ref="root">
   <p class="un-h-500px">
    Scroll me down!
   </p>
   <div
    v-intersection-observer="[onIntersectionObserver, { root }]"
    class="un-h-200px"
   >
    <p>Hello world!</p>
   </div>
  </div>
 </b-div>
 <div>{{ isVisible }}</div>
</template>

ResizeObserver

vue
<script setup lang="ts">
const text = ref('');

function onResizeObserver(entries) {
 const [entry] = entries;
 const { width, height } = entry.contentRect;
 text.value = `width: ${width}, height: ${height}`;
}
</script>

<template>
 <textarea v-resize-observer="onResizeObserver">
  {{ text }}
 </textarea>
</template>

Scroll

1
2
3
4
5
6
1
2
3
4
5
6
vue
<script setup lang="ts">
import type { UseScrollReturn } from '@vueuse/core';

const data = ref([1, 2, 3, 4, 5, 6]);
const state = ref();

function onScroll(scroll: UseScrollReturn) {
 state.value = scroll; // {x, y, isScrolling, arrivedState, directions}
}
</script>

<template>
 <b-div
  v-scroll="onScroll"
  overflow="auto"
  class="un-h-100px"
 >
  <div
   v-for="item in data"
   :key="item"
  >
   {{ item }}
  </div>
 </b-div>

 <!-- with options -->
 <b-div
  v-scroll="[onScroll, { throttle: 10 }]"
  overflow="auto"
  class="un-h-100px"
 >
  <div
   v-for="item in data"
   :key="item"
  >
   {{ item }}
  </div>
 </b-div>
 <pre>{{ state }}</pre>
</template>