Composables — Visión General
Los composables son funciones que encapsulan lógica reactiva reutilizable. Son el mecanismo principal para compartir comportamiento entre componentes sin herencia ni mixins. En Vue 3 con Composition API, reemplazan completamente a los mixins de Vue 2.
En este proyecto hay 26+ composables. Se auto-importan — no necesitas escribir import, solo los invocas directamente:
const { data, isPending } = useServices('getCourses')const { showAlert } = useShowAlert()const { showModal, useOpenModal } = useModal()Los más importantes (uso diario)
Section titled “Los más importantes (uso diario)”Estos son los composables que usarás constantemente. Cada uno tiene su propia página de documentación con ejemplos detallados:
| Composable | Propósito | Cuándo lo necesitas |
|---|---|---|
useServices | Obtener datos del backend con fallback i18n | Siempre que muestres datos del CMS |
useShowAlert | Mostrar toasts de éxito/error/warning | Tras operaciones de usuario, errores de API |
useBuyFlow | Orquestar el flujo de compra completo | Botones de “Comprar”, cards de producto |
useForm | Gestión de formularios con validación | Registro, perfil, contacto, checkout |
useModal | Controlar apertura/cierre de modales | Cualquier popup, confirmación, o overlay |
useGlobalSearch | Búsqueda con debounce y filtrado reactivo | Barra de búsqueda, filtros de listados |
useLocales | Cargar traducciones JSON por página | Contenido de páginas, secciones largas |
useCheckout | Crear sesiones de Stripe | Internamente por useBuyFlow — raro usarlo directo |
useLanguageChanger | Cambiar idioma global del usuario | Selector de idioma en la navbar |
Composables de ecom-components (auto-importados)
Section titled “Composables de ecom-components (auto-importados)”La librería @alebat-education/ecom-components también exporta composables que se registran automáticamente:
| Composable | Propósito | Ejemplo de uso |
|---|---|---|
useCardBehavior() | Estados de interacción de cards (hover, pressed, focus) | Animaciones de cards personalizadas |
useCustomSwiper() | Configuración pre-armada para sliders con Swiper | Carruseles de productos, media galleries |
usePdfEngine() | Motor de renderizado de PDFs (pdfjs-dist) | Lector de libros digitales |
useTimerCountdown() | Cuenta regresiva con limpieza automática al desmontar | Eventos en vivo, ofertas temporales |
Estos NO están en la carpeta composables/ del proyecto — vienen del paquete. Los documentamos aquí para que sepas que existen.
Composables que NO deberías llamar directamente
Section titled “Composables que NO deberías llamar directamente”Algunos composables son internos — los usan otros composables o el sistema, no tú:
| Composable | Lo usa | Por qué no llamarlo directo |
|---|---|---|
useCheckout | useBuyFlow | El buy flow maneja toda la orquestación; checkout solo es un paso |
useElevenLabsAgent | Sistema de chat AI | Configuración compleja de agente, no tiene sentido standalone |
Cuándo crear un composable nuevo
Section titled “Cuándo crear un composable nuevo”Crea un composable cuando:
- La lógica se repite en 2+ componentes — el caso más claro. Si copias y pegas un bloque de refs + computeds + watchers, es candidato a composable.
- Un componente tiene demasiadas responsabilidades — si un layout tiene 300+ líneas de
<script setup>, extrae bloques lógicos coherentes a composables. - Necesitas compartir estado reactivo entre hermanos — cuando dos componentes que no son padre/hijo necesitan reaccionar al mismo estado (sin usar un store global).
- La lógica involucra efectos secundarios complejos — timers, event listeners, WebSocket connections, intersection observers. Un composable puede manejar el cleanup automáticamente con
onUnmounted.
No crees un composable si:
- Solo necesitas una función pura (eso es un
util, no un composable) - El estado solo lo necesita un componente (déjalo inline en el componente)
- Ya existe un composable que hace lo mismo (revisa la lista arriba primero)
Estructura de un composable
Section titled “Estructura de un composable”export const useWebinarFilters = () => { // 1. Estado reactivo const searchQuery = ref('') const selectedCategory = ref<string | null>(null) const results = ref<WebinarItem[]>([])
// 2. Computeds derivados const filteredResults = computed(() => { let filtered = results.value if (searchQuery.value) { filtered = filtered.filter(w => w.title.toLowerCase().includes(searchQuery.value.toLowerCase()) ) } if (selectedCategory.value) { filtered = filtered.filter(w => w.category === selectedCategory.value) } return filtered })
const hasResults = computed(() => filteredResults.value.length > 0)
// 3. Métodos / acciones function clearFilters() { searchQuery.value = '' selectedCategory.value = null }
// 4. Retorna solo lo que el consumidor necesita return { searchQuery, selectedCategory, filteredResults, hasResults, clearFilters, }}Reglas:
- Siempre empieza con
use+ nombre descriptivo en PascalCase - Exporta como
export const useNombre = () => { ... } - Retorna un objeto con las refs, computeds y funciones que el consumidor necesita
- No necesitas registrarlo — Nuxt lo auto-importa al estar en
composables/
Diferencia entre composable, util, y servicio
Section titled “Diferencia entre composable, util, y servicio”| Composable | Util | Servicio | |
|---|---|---|---|
| Ubicación | composables/ | utils/ | services/ |
| Usa reactividad | Sí (ref, computed, watch) | No — funciones puras | No directamente |
| Auto-importado | Sí | Sí | No (se accede via useServices) |
| Ejemplo | useForm(), useModal() | formatDate(), slugify() | getCourses(), getUserLogin() |
| Convención de nombre | useAlgo | nombreFuncion | getNombreRecurso |