Vue Hooks ​
Modularous ships 38 composable hooks under vue/src/js/hooks/. They are the primary building blocks for form inputs, table behaviour, UI state, and media management.
All hooks are exported from @/hooks/ (the alias for vue/src/js/hooks/index.js).
import { useAlert, useAuthorization, useModal } from '@/hooks'Getting Started ​
Every hook in Modularous follows the same high-level shape:
const { state, methods } = useXxx(props, emit?)- Inputs: either component
props(reactive), a plain options object, or(props, emit)for hooks that forwardv-modelupdates. - Output: a plain object you destructure into reactive state (
ref/computed) and methods. Neverreactive()-wrapped — destructure freely without losing reactivity. - Lifecycle: hooks that touch the DOM, the store, or globals (modals, alerts, media library) are safe to call inside
setup(); they internally handleonMounted/onUnmounted.
Minimal component ​
<script setup>
import { useModal, useAlert } from '@/hooks'
const { isOpen, open, close } = useModal()
const { success, error } = useAlert()
async function onSubmit() {
try {
await saveRecord()
success('Saved')
close()
} catch (e) {
error(e.message)
}
}
</script>
<template>
<v-btn @click="open">Edit</v-btn>
<v-dialog v-model="isOpen">...</v-dialog>
</template>Composition Patterns ​
Hooks are small on purpose so they can be layered. The most common compositions follow.
Forms: useForm + useInput + useValidation ​
useForm owns submit, validation, and schema/model sync. Individual inputs use useInput to read their schema slice, and useValidation to bind rules.
// Form.vue (orchestrator)
const { formData, submit, isSubmitting, errors } = useForm(props, emit)
// Inside any input component
const { modelValue, boundProps } = useInput(props, emit)
const { rules } = useValidation(props.schema)See useForm, useInput, useValidation.
Inputs that fetch: useInput + useInputFetch ​
Select / autocomplete inputs with remote data combine both:
const { modelValue, boundProps } = useInput(props, emit)
const { items, loading, search } = useInputFetch(props.schema)Tables: useTable as a super-composable ​
useTable already wires the 11 sub-hooks. You rarely compose sub-hooks yourself — read them to extend a behaviour, not to rebuild it.
// Table.vue
const table = useTable(props)
// table.headers, table.filters, table.items, table.editItem, ...Only reach for useTableHeaders, useTableFilters, etc. when building a custom table UI that reuses part of the behaviour.
Media uploads: input + hook pair ​
| Input component | Hook |
|---|---|
VInputImage | useImage — Media library selection |
VInputFile | useFile — Media library selection |
VInputFilepond | useFilepond — direct FilePond upload |
const { open, selected } = useMediaLibrary({ type: 'image' })
const { modelValue, processFile } = useFilepond(props, emit)Global UI services ​
useAlert, useDynamicModal, useSidebar are singletons backed by Vuex / provide-inject. Call them from anywhere without wiring:
const { info, success, error } = useAlert()
const { open: openModal } = useDynamicModal()App state ​
useConfig, useUser, useLocale, useAuthorization, useCache read from the Vuex store. They are the idiomatic way to access global state — avoid reading window.__* or store.state.* directly.
const { user, can } = useUser()
const { t, locale } = useLocale()
const { config } = useConfig()
if (can('edit', 'posts')) { /* ... */ }Conventions ​
Naming ​
| Pattern | Meaning |
|---|---|
useXxx | Main composable (imported from @/hooks) |
makeXxxProps | Vuetify propsFactory export — reuse props on a component |
useXxx/useYyy | Sub-hook (in subfolder, e.g. hooks/table/useTableHeaders) |
Props factories ​
Most hooks that consume props also export a makeXxxProps factory. Use it when building a component that should accept the same props:
import { makeModalProps } from '@/hooks/useModal'
export default defineComponent({
props: {
...makeModalProps(),
title: String,
},
setup(props, ctx) {
const modal = useModal(props)
// ...
},
})Return shape ​
Hooks return plain objects. Do not wrap them in reactive() at the call site — individual ref / computed values stay reactive when destructured.
// Correct
const { isOpen, open, close } = useModal()
// Wrong — loses reactivity on destructure
const modal = reactive(useModal())When a hook isn't the answer ​
- For one-off local state, plain
refis fine; don't invent a hook for two lines. - For pure utilities (formatters, validators), put them in
vue/src/js/utils/— hooks are for stateful or reactive behaviour.
Full Hook Reference ​
| Hook | File | Purpose |
|---|---|---|
| useActiveTableItem | useActiveTableItem.js | Active row / detail-panel state in tables |
| useAlert | useAlert.js | Trigger global alert notifications |
| useAuthorization | useAuthorization.js | Permission and role checks in Vue |
| useCache | useCache.js | Client-side key-value cache via Vuex |
| useCastAttributes | useCastAttributes.js | Dynamic $notation attribute interpolation |
| useCurrency | useCurrency.js | Currency value helpers |
| useCurrencyNumber | useCurrencyNumber.js | Number formatting with currency |
| useConfig | useConfig.js | Access app config from Vuex |
| useDraggable | useDraggable.js | Drag-and-drop (Sortable.js) props and state |
| useDynamicModal | useDynamicModal.js | Inject-based global modal service |
| useFile | useFile.js | File media-library input state |
| useFilepond | useFilepond.js | FilePond upload props and validation rules |
| useForm | useForm.js | Top-level form state, submit, validation |
| useFormBase | useFormBase.js | FormBase flattening and field iteration |
| useFormBaseLogic | useFormBaseLogic.js | FormBase rendering logic |
| useFormatter | useFormatter.js | Table column value formatters |
| useImage | useImage.js | Image media-library input state |
| useInertiaRequests | useInertiaRequests.js | Inertia.js in-flight request state |
| useInput | useInput.js | Base input state, modelValue, schema binding |
| useInputFetch | useInputFetch.js | Paginated remote data fetch for select inputs |
| useInputHandlers | useInputHandlers.js | Slot-driven input click handlers |
| useItemActions | useItemActions.js | Form action buttons (request / modal / download / blank) |
| useLocale | useLocale.js | Active locale helpers |
| useMediaItems | useMediaItems.js | Selected media item list management |
| useMediaLibrary | useMediaLibrary.js | Open/close media library modal |
| useModal | useModal.js | Modal open/close, width, fullscreen state |
| useModelValue | useModelValue.js | v-model two-way binding helper |
| useModule | useModule.js | Module name translation and metadata |
| useNavigationLayout | useNavigationLayout.js | Topbar / bottom-nav config merging |
| useRandKey | useRandKey.js | Unique component instance key |
| useRepeater | useRepeater.js | Repeater block state, add / delete / duplicate |
| useRoot | useRoot.js | Vuetify / root instance access |
| useSidebar | useSidebar.js | Sidebar open/close, rail, resize |
| useSvg | useSvg.js | SVG symbol existence and locale lookup |
| useTable | useTable.js | Main data-table composable |
| useUser | useUser.js | Authenticated user state and authorization proxy |
| useValidation | useValidation.js | Validation rules and rule generator |
Props Factories ​
Many hooks export a makeXxxProps factory built with Vuetify's propsFactory. Use these when building components that accept the same props as a hook:
import { makeModalProps } from '@/hooks/useModal'
import { makeRepeaterProps } from '@/hooks/useRepeater'
import { makeFilepondProps } from '@/hooks/useFilepond'Hook Layers ​
App state useConfig · useUser · useLocale · useAuthorization · useCache
UI chrome useSidebar · useNavigationLayout · useModal · useDynamicModal
Notifications useAlert
Form useForm · useFormBase · useInput · useModelValue · useValidation
Inputs useFile · useImage · useFilepond · useRepeater · useInputFetch
useInputHandlers · useDraggable
Table useTable · useActiveTableItem · useItemActions · useFormatter
Utilities useCastAttributes · useModule · useRandKey · useRoot · useSvg
useInertiaRequestsTable Sub-hooks ​
useTable is composed from 11 internal sub-hooks. See the Table Sub-hooks Overview for details.
| Sub-hook | Purpose |
|---|---|
| useTableActions | Toolbar / bulk action props |
| useTableFilters | Search, status tabs, advanced filters |
| useTableForms | Create/edit form state |
| useTableGroup | Client-side column grouping |
| useTableHeaders | Column visibility and localStorage |
| useTableItem | Edited item and soft-delete detection |
| useTableItemActions | Per-row action dispatch |
| useTableIterator | Iterator (card/list) layout actions |
| useTableModals | Delete / custom / show modals |
| useTableNames | i18n titles and dialog text |
| useTableState | URL/localStorage state persistence |
Utility Sub-hooks ​
Four small utility composables are available under vue/src/js/hooks/utils/. See the Utils Overview for details.
| Sub-hook | Purpose |
|---|---|
| useBadge | Badge visibility and props for action buttons |
| useGenerate | Button prop generation with Inertia-aware href handling |
| usePagination | Infinite-scroll / load-more pagination state |
| useSelect | Select input prop definitions (makeSelectProps) |