Composables: Reusable Composition Functions
Extract reactive logic into composable functions prefixed with use, share them across components, and understand how they differ from mixins.
Composables: Reusable Composition Functions is a free Frontend Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What Are Composables?
A composable is a function that uses Composition API hooks to encapsulate and reuse stateful logic. They're the Composition API equivalent of mixins — but without namespace collisions or hidden dependencies.
A Basic Composable
Extract reactive logic into a function starting with use. It can use ref, computed, watch, and lifecycle hooks — just like setup().
// useCounter.ts
import { ref } from 'vue';
export function useCounter(initial = 0) {
const count = ref(initial);
const increment = () => count.value++;
const decrement = () => count.value--;
const reset = () => (count.value = initial);
return { count, increment, decrement, reset };
}
// Usage in any component:
const { count, increment } = useCounter(10);Composable with Lifecycle Hooks
Composables can register lifecycle hooks. The hooks fire relative to the component that calls the composable.
// useOnlineStatus.ts
import { ref, onMounted, onUnmounted } from 'vue';
export function useOnlineStatus() {
const isOnline = ref(navigator.onLine);
const update = () => (isOnline.value = navigator.onLine);
onMounted(() => {
window.addEventListener('online', update);
window.addEventListener('offline', update);
});
onUnmounted(() => {
window.removeEventListener('online', update);
window.removeEventListener('offline', update);
});
return { isOnline };
}Composable with Async (useFetch)
Composables work great for async data fetching — encapsulate the loading/error/data pattern.
// useFetch.ts
export function useFetch<T>(url: MaybeRef<string>) {
const data = ref<T | null>(null);
const loading = ref(true);
const error = ref<string | null>(null);
watchEffect(async (onCleanup) => {
const controller = new AbortController();
onCleanup(() => controller.abort());
loading.value = true;
error.value = null;
try {
const res = await fetch(unref(url), { signal: controller.signal });
data.value = await res.json();
} catch (e: any) {
if (e.name !== 'AbortError') error.value = e.message;
} finally { loading.value = false; }
});
return { data, loading, error };
}Accepting Refs as Parameters
Composables can accept either plain values or refs as arguments using MaybeRef<T> (= T | Ref<T>) and unref() to access the value.
import type { MaybeRef } from 'vue';
import { unref } from 'vue';
function useDouble(num: MaybeRef<number>) {
return computed(() => unref(num) * 2);
}Composables vs Mixins
Mixins inject properties into the component without clear origin. Composables are explicit — you see exactly what a composable provides from its return value. Multiple composables can be used without naming conflicts.
Sharing State Between Components
If a composable calls ref() inside itself, each component that calls the composable gets its own state. To share state across components, call ref() at module scope (outside the composable function).
// Shared state (module-level):
const sharedCount = ref(0);
export function useSharedCounter() {
// All callers share the same sharedCount
return { count: sharedCount, increment: () => sharedCount.value++ };
}Composable Libraries
VueUse (vueuse.org) provides 200+ ready-to-use composables: useLocalStorage, useFetch, useDark, useIntersectionObserver, and many more. Always check VueUse before writing from scratch.
Testing Composables
Test composables by calling them inside a small test component or using @vue/test-utils mountedApp pattern. Vitest integrates well with Vue's reactivity system.
Composable Naming Conventions
Always prefix with use (useAuth, useFetch, useMediaQuery). Keep them focused on a single concern. Avoid composables that do too many unrelated things.
Composable Organisation
Create a composables/ folder. One file per composable. Index file re-exports all composables. This mirrors how custom hooks are organised in React projects.
Quick Check
What is the key advantage of composables over Vue mixins?
Recap: Vue Composables
A composable is a use-prefixed function that calls Composition API hooks. Each caller gets independent state unless you use module-level refs. Accept refs or values with MaybeRef + unref. Register lifecycle hooks inside composables. Check VueUse before writing your own. Explicit return values prevent mixin-style namespace pollution.
Frequently asked questions
Is the “Composables: Reusable Composition Functions” lesson free?
Yes — the full text of “Composables: Reusable Composition Functions” is free to read here on the web, and the Frontend Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Frontend Academy course, upgrade to CoddyKit PRO.
What will I learn in “Composables: Reusable Composition Functions”?
Extract reactive logic into composable functions prefixed with use, share them across components, and understand how they differ from mixins. You practise Frontend Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Frontend Academy?
No prior experience is required. Frontend Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Composables: Reusable Composition Functions” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Frontend Academy lesson?
Yes. Every Frontend Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- ref() and reactive()
- computed() and watch()
- The setup() Function and script setup
- Composables: Reusable Composition Functions