본문으로 건너뛰기
cccskills
무료GitHub에 공개

web-utilities-vueuse

VueUse composable utility collection for Vue 3 - browser, sensor, network, state, animation, and component utilities

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(8)

  • SKILL.md11.1 KB
  • examples/component.md5.6 KB
  • examples/core.md5.3 KB
  • examples/network.md5.0 KB
  • examples/sensors.md4.6 KB
  • examples/state.md4.2 KB
  • metadata.yaml518 B
  • reference.md7.3 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

VueUse Composable Patterns

Quick Guide: VueUse wraps browser APIs, sensors, network transports and small state utilities as composables that return refs and clean themselves up on scope disposal. Import each function by name from @vueuse/core — the library is large and entirely tree-shakeable. Two things decide whether a call works: it must run synchronously during setup, because teardown binds to the effect scope that is current at call time; and browser-API composables behave differently on a server, some returning a safe default and some needing client-only rendering.

Detailed Resources:


Which path applies

Server rendering is the branch, because it changes which composables are usable at all.

  • Client-rendered only — every composable is available; call it and read the ref.
  • Server-rendered — storage, media-query and preference composables return their declared default on the server and hydrate on the client. Sensor and transport composables (useMouse, useScroll, useIntersectionObserver, useWebSocket, useClipboard) need a DOM and belong inside a client-only boundary. The table in reference.md says which is which.
  • The composable wraps an API not every browser has — read its isSupported ref and render a fallback, rather than assuming the call worked.

<critical_requirements>

Before writing VueUse code

Call composables synchronously during setup. Teardown registers against the effect scope that is current when the composable runs, so a call inside an await, a setTimeout or an event handler has no scope to attach to and its listeners are never removed.

Import each composable by name from @vueuse/core. A namespace import pulls the whole library past the bundler's tree-shaker, and the library is several hundred functions.

Read isSupported where the composable wraps an optional browser API — clipboard, share, geolocation, battery, wake lock. It exists because the API can be absent or permission-gated, and the fallback path is what the user sees when it is.

Decide the server behaviour before the composable ships. Each one either returns a declared default on the server or needs a client-only boundary; guessing produces a hydration mismatch, which reports as a rendering bug far from its cause.

</critical_requirements>


Auto-detection: VueUse, vueuse, @vueuse/core, @vueuse/integrations, useLocalStorage, useSessionStorage, useClipboard, useFetch, createFetch, useMouse, useMouseInElement, useScroll, useIntersectionObserver, useResizeObserver, useElementVisibility, useElementSize, useMediaQuery, usePreferredDark, useDark, useToggle, useCounter, useCycleList, useWebSocket, useEventSource, useEventListener, useTransition, useRafFn, createGlobalState, useRefHistory, useManualRefHistory, syncRef, useVModel, useVirtualList, onClickOutside, onKeyStroke, useDebounceFn, watchDebounced

Applies to:

  • Browser APIs as reactive refs — storage, clipboard, media queries, preferences
  • Sensor readings — pointer position, scroll, intersection, element size and visibility
  • Reactive HTTP, WebSocket and server-sent-event transports
  • Small shared state, undo history, and ref synchronisation
  • Component helpers — two-way binding, virtual lists, outside clicks, key strokes, value animation

Handled elsewhere:

  • Server-state caching, invalidation and optimistic updates — a distinct problem that a reactive request wrapper does not solve
  • Application state architecture — actions, modules and devtools belong to whatever owns the store
  • The reactivity primitives themselves — ref, computed, watch and effect scopes are the framework's, and these composables are built on top of them
  • Routing, forms and validation — none of these composables model any of it

<philosophy>

Each composable owns one concern and its own teardown. That is the whole contract: it returns refs, it registers whatever listener or observer it needs, and it disposes of them when the surrounding effect scope ends. Nothing is registered globally and nothing needs a plugin.

The consequence is that the call site owns the lifetime. Two components calling useMouse() get two independent listeners, both cleaned up separately — which is why sharing one instance is an explicit act (createGlobalState, or a composable of your own) rather than something the library does for you.

</philosophy>
<patterns>

Core patterns

Pattern 1: Reactive storage

import { useLocalStorage } from "@vueuse/core";

const settings = useLocalStorage("app-settings", DEFAULTS, {
  mergeDefaults: true, // stored data gains fields added to DEFAULTS since it was written
});

settings.value.theme = "dark"; // persists, and other tabs see it
settings.value = null; // removes the key entirely — it does not store null

Serialisation, cross-tab sync via the storage event, and a server-side default all come with it. Full code: examples/core.md


Pattern 2: Listeners that remove themselves

import { useEventListener } from "@vueuse/core";
import { useTemplateRef } from "vue";

const dropZone = useTemplateRef("drop-zone");

useEventListener(window, "resize", onResize);
useEventListener(dropZone, "drop", onDrop); // waits for the ref to be populated

Passing a ref rather than an element is the point: the composable attaches once the element exists and re-attaches if it changes, so there is no mount-order problem to solve by hand.

Full code: examples/core.md


Pattern 3: Media queries and colour scheme

import {
  useMediaQuery,
  usePreferredDark,
  useDark,
  useToggle,
} from "@vueuse/core";

const isMobile = useMediaQuery("(max-width: 768px)");
const prefersDark = usePreferredDark(); // reads the OS setting
const isDark = useDark(); // reads it, persists an override, toggles a class
const toggleDark = useToggle(isDark);

usePreferredDark observes; useDark also writes — it persists the choice and toggles a class on the root element, so the class name it uses has to match what the stylesheet expects.

Full code: examples/core.md


Pattern 4: Sensors

import { useScroll, useIntersectionObserver } from "@vueuse/core";

const { y, directions, arrivedState } = useScroll(container);

useIntersectionObserver(
  sentinel,
  ([entry]) => {
    if (entry?.isIntersecting) loadMore();
  },
  { rootMargin: "200px" },
); // fire before it reaches the viewport

arrivedState and directions are the parts worth knowing — they replace the offset arithmetic that scroll handlers usually get wrong at the edges.

Full code: examples/sensors.md


Pattern 5: Reactive fetch

import { useFetch } from "@vueuse/core";

const url = computed(() => `/api/users/${userId.value}`);

// Re-runs whenever `url` changes, and aborts the previous request
const { data, isFetching, error, abort } = useFetch(url, {
  refetch: true,
}).json<User>();

// Manual: no request until execute() is called
const { execute } = useFetch("/api/users", { immediate: false })
  .post(body)
  .json();

The chained .json<T>() is what types data; without it you hold a Ref<string | null>.

Full code: examples/network.md


Pattern 6: Shared state without a store library

import { createGlobalState } from "@vueuse/core";

export const useGlobalCounter = createGlobalState(() => {
  const count = shallowRef(0);
  const double = computed(() => count.value * 2);
  const increment = () => count.value++;

  return { count, double, increment };
});

The factory runs once and every caller gets the same refs. Returning actions rather than the bare writable refs is what keeps the mutation points countable.

Full code: examples/state.md


Pattern 7: Two-way binding

import { useVModel } from "@vueuse/core";

const value = useVModel(props, "modelValue", emit);
const count = useVModel(props, "count", emit); // named v-model

value.value = next; // emits update:modelValue

One line where the get/set computed is five, and the event name is derived rather than typed out — which is where the typo used to live.

Full code: examples/component.md

</patterns>

<red_flags>

Red flags

Breaks at runtime:

  • A composable called inside await, setTimeout or an event handler binds to no effect scope: its listeners and observers are never disposed of, and the component leaks them on every mount.
  • A sensor or transport composable called during server rendering touches window or document and throws. Move it behind a client-only boundary.
  • Using a browser-API composable without checking isSupported fails on the browsers and contexts where the API is missing or permission-gated — clipboard write outside a user gesture is the usual first case.
  • A namespace import of @vueuse/core defeats tree-shaking and ships the whole library.

Surprising behaviour:

  • useLocalStorage(...).value = null deletes the key rather than storing null. Store an explicit empty value where the key must survive.
  • createGlobalState is a singleton for the module's lifetime — it survives every component unmounting and is cleared only by a reload, which makes it a poor fit for per-user state that must reset on sign-out.
  • useIntersectionObserver invokes its callback once on registration with the element's current state, so "became visible" needs comparing against the previous value rather than trusting the first call.
  • useFetch fires on mount by default; refetch: true adds re-firing on URL change without removing the initial one. immediate: false is what makes it fully manual.
  • useWebSocket reconnects by default, so a server rejecting the connection produces a retry loop until autoReconnect: false or a bounded retries says otherwise.
  • useRefHistory snapshots every change, which on a text input is one entry per keystroke. Give it a capacity, or use useManualRefHistory and commit at save points.
  • shallowRef avoids deep-reactivity cost on large objects, but a composable that persists or watches nested changes — useLocalStorage among them — needs the deep tracking to see them at all. Choose per composable rather than as a blanket rule.

</red_flags>

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

agents-inc의 스킬 모두 보기

이 스킬의 문제 신고