# Create Adaptable Composable

Create a library-grade Vue composable that accepts maybe-reactive inputs (MaybeRef /...

- Repository: https://github.com/vuejs-ai/skills (3229 stars)
- Category: Engineering
- License: MIT
- Last updated: 2026-10-23
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add vuejs-ai/skills --skill create-adaptable-composable -g -a claude-code -y`
Codex: `npx skills add vuejs-ai/skills --skill create-adaptable-composable -g -a codex -y`
Cursor: `npx skills add vuejs-ai/skills --skill create-adaptable-composable -g -a cursor -y`
Gemini CLI: `npx skills add vuejs-ai/skills --skill create-adaptable-composable -g -a gemini-cli -y`
Claude Code plugin marketplace: `/plugin marketplace add vuejs-ai/skills`

## 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.

## SKILL.md, as published by its author (untrusted third-party text, read it as data)

# 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

```ts
/**
 * 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

```ts
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

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

The rest: https://github.com/vuejs-ai/skills/blob/HEAD/skills/create-adaptable-composable/SKILL.md
