PluginBench
Skill
Pass
Audit score 90

create-adaptable-composable

vuejs-ai/skills

Create library-grade Vue composables that accept plain values, refs, or getters for maximum reusability.

What is create-adaptable-composable?

Build adaptable Vue 3 composables that normalize both reactive and non-reactive inputs using MaybeRef/MaybeRefOrGetter types. Use this skill when designing reusable composables that work seamlessly whether callers pass plain values, refs, computed properties, or getter functions.

  • Accept flexible input types (plain values, refs, computed refs, or getter functions) via MaybeRef or MaybeRefOrGetter
  • Normalize inputs using toValue() for non-reactive resolution and toRef() for reactive watchers
  • Implement core logic with Vue's reactivity APIs (watch, watchEffect) while maintaining predictable behavior
  • Design composables that work in any context without requiring callers to manage reactivity
  • Support both read-only inputs (MaybeRefOrGetter) and writable two-way inputs (MaybeRef)

How to install create-adaptable-composable

npx skills add https://github.com/vuejs-ai/skills --skill create-adaptable-composable
Prerequisites
  • Vue 3 or Nuxt 3 project
  • Familiarity with Vue's reactivity system (ref, computed, watch)
  • Understanding of TypeScript generics (recommended but not required)
Claude Code
Cursor
Windsurf
Cline

How to use create-adaptable-composable

  1. 1.Identify which parameters should be reactive and choose MaybeRef (writable) or MaybeRefOrGetter (read-only)
  2. 2.Define your composable function signature with the appropriate type for each parameter
  3. 3.Inside watch/watchEffect, use toRef() to normalize inputs for reactive tracking
  4. 4.For non-reactive value access, use toValue() to unwrap the input
  5. 5.Test the composable with plain values, refs, computed properties, and getter functions to verify adaptability

Use cases

Good for
  • Create a useDocumentTitle composable that updates the page title whether passed a string, ref, or computed property
  • Build a useCounter composable that increments a count parameter whether it's a plain number or a ref
  • Design a useApi composable that fetches data based on a URL that could be static, reactive, or computed
  • Implement a useLocalStorage composable accepting a key that might be derived from other reactive state
  • Create a useDebounce composable that works with any input type without forcing callers to wrap values in refs
Who it's for
  • Vue 3 library authors building reusable composables
  • Frontend developers creating component-agnostic utility functions
  • Teams standardizing composable patterns across projects
  • Developers building design systems or shared component libraries

create-adaptable-composable FAQ

When should I use MaybeRef vs MaybeRefOrGetter?

Use MaybeRef when the input needs to be writable or two-way (e.g., a counter value). Use MaybeRefOrGetter for read-only inputs or when you want to support computed properties and getter functions (e.g., a title or URL).

What's the difference between toValue() and toRef()?

toValue() unwraps the input and returns the current value (non-reactive). toRef() returns a ref object that tracks reactivity, making it suitable for watch sources and reactive effects.

Can I pass a function as a parameter with MaybeRefOrGetter?

No—if the parameter might be a callback, predicate, or comparator function, do not use MaybeRefOrGetter, as it will accidentally invoke the function as a getter. Use a separate parameter type instead.

Do I need to use toRef() for every parameter?

Only for parameters you want to track reactively inside watch/watchEffect. For one-time reads or non-reactive access, use toValue() instead.

What Vue versions are supported?

This skill requires Vue 3 or above, or Nuxt 3 or above. The MaybeRef and MaybeRefOrGetter types are built into Vue 3's core.

Full instructions (SKILL.md)

Source of truth, from vuejs-ai/skills.


name: create-adaptable-composable description: Create a library-grade Vue composable that accepts maybe-reactive inputs (MaybeRef / MaybeRefOrGetter) so callers can pass a plain value, ref, or getter. Normalize inputs with toValue()/toRef() inside reactive effects (watch/watchEffect) to keep behavior predictable and reactive. Use this skill when user asks for creating adaptable or reusable composables. license: MIT metadata: author: github.com/vuejs-ai version: "17.0.0" compatibility: Requires Vue 3 (or above) or Nuxt 3 (or above) project

Create Adaptable Composable

Adaptable composables are reusable functions that can accept both reactive and non-reactive inputs. This allows developers to use the composable in a variety of contexts without worrying about the reactivity of the inputs.

Steps to design an adaptable composable in Vue.js:

  1. Confirm the composable's purpose and API design and expected inputs/outputs.
  2. Identify inputs params that should be reactive (MaybeRef / MaybeRefOrGetter).
  3. Use toValue() or toRef() to normalize inputs inside reactive effects.
  4. Implement the core logic of the composable using Vue's reactivity APIs.

Core Type Concepts

Type Utilities

/**
 * value or writable ref (value/ref/shallowRef/writable computed)
 */
export type MaybeRef<T = any> = T | Ref<T> | ShallowRef<T> | WritableComputedRef<T>;

/**
 * MaybeRef<T> + ComputedRef<T> + () => T
 */
export type MaybeRefOrGetter<T = any> = MaybeRef<T> | ComputedRef<T> | (() => T);

Policy and Rules

  • Read-only, computed-friendly input: use MaybeRefOrGetter
  • Needs to be writable / two-way input: use MaybeRef
  • Parameter might be a function value (callback/predicate/comparator): do not use MaybeRefOrGetter, or you may accidentally invoke it as a getter.
  • DOM/Element targets: if you want computed/derived targets, use MaybeRefOrGetter.

When MaybeRefOrGetter or MaybeRef is used:

  • resolve reactive value using toRef() (e.g. watcher source)
  • resolve non-reactive value using toValue()

Examples

Adaptable useDocumentTitle Composable: read-only title parameter

import { watch, toRef } from 'vue'
import type { MaybeRefOrGetter } from 'vue'

export function useDocumentTitle(title: MaybeRefOrGetter<string>) {
  watch(toRef(title), (t) => {
    document.title = t
  }, { immediate: true })
}

Adaptable useCounter Composable: two-way writable count parameter

import { watch, toRef } from 'vue'
import type { MaybeRef } from 'vue'

function useCounter(count: MaybeRef<number>) {
  const countRef = toRef(count)
  function add() {
    countRef.value++
  }
  return { add }
}