Reusable Logic in composables/
Custom Composables
Any file under composables/ exports named composables that are auto-imported across the app, perfect for wrapping useState, useFetch, or any reusable reactive logic.
What you'll learn
- Write a composable file under the composables directory
- Compose useState and useFetch inside a single composable
- Type your return shape so consumers get full inference
The composables/ directory is Nuxt’s home for shared reactive logic. Every named export becomes
auto-imported app-wide — no manual import statements needed.
A Simple Composable
// composables/useUser.ts
export interface User {
id: string
name: string
}
export const useUser = () => useState<User | null>('user', () => null) Now any page or component can grab it:
<script setup lang="ts">
const user = useUser()
</script>
<template>
<p v-if="user">Hi {{ user.name }}</p>
<p v-else>Not signed in</p>
</template> Composing Other Composables
Composables compose. Here is one that fetches the current user and caches the result:
// composables/useCurrentUser.ts
export const useCurrentUser = async () => {
const user = useUser()
if (!user.value) {
const { data } = await useFetch<User>('/api/me')
user.value = data.value
}
return user
} Returning Refs Preserves Reactivity
If you return a plain value, consumers get a snapshot. Return refs (or reactive objects) to keep reactivity intact:
// composables/useCart.ts
export const useCart = () => {
const items = useState<string[]>('cart', () => [])
const count = computed(() => items.value.length)
const add = (id: string) => items.value.push(id)
return { items, count, add }
} Naming Conventions
- File names use kebab-case or camelCase — Nuxt scans the default export and any named exports.
- By convention, composables start with
use(matches Vue’s rules-of-hooks style). - Nested folders work, but only the leaf file names need to be unique.