Skip to content

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

js
import { useAlert, useAuthorization, useModal } from '@/hooks'

Getting Started ​

Every hook in Modularous follows the same high-level shape:

js
const { state, methods } = useXxx(props, emit?)
  • Inputs: either component props (reactive), a plain options object, or (props, emit) for hooks that forward v-model updates.
  • Output: a plain object you destructure into reactive state (ref / computed) and methods. Never reactive()-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 handle onMounted / onUnmounted.

Minimal component ​

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

js
// 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:

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

js
// 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 componentHook
VInputImageuseImage — Media library selection
VInputFileuseFile — Media library selection
VInputFileponduseFilepond — direct FilePond upload
js
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:

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

js
const { user, can } = useUser()
const { t, locale } = useLocale()
const { config } = useConfig()

if (can('edit', 'posts')) { /* ... */ }

Conventions ​

Naming ​

PatternMeaning
useXxxMain composable (imported from @/hooks)
makeXxxPropsVuetify propsFactory export — reuse props on a component
useXxx/useYyySub-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:

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

js
// 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 ref is 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 ​

HookFilePurpose
useActiveTableItemuseActiveTableItem.jsActive row / detail-panel state in tables
useAlertuseAlert.jsTrigger global alert notifications
useAuthorizationuseAuthorization.jsPermission and role checks in Vue
useCacheuseCache.jsClient-side key-value cache via Vuex
useCastAttributesuseCastAttributes.jsDynamic $notation attribute interpolation
useCurrencyuseCurrency.jsCurrency value helpers
useCurrencyNumberuseCurrencyNumber.jsNumber formatting with currency
useConfiguseConfig.jsAccess app config from Vuex
useDraggableuseDraggable.jsDrag-and-drop (Sortable.js) props and state
useDynamicModaluseDynamicModal.jsInject-based global modal service
useFileuseFile.jsFile media-library input state
useFileponduseFilepond.jsFilePond upload props and validation rules
useFormuseForm.jsTop-level form state, submit, validation
useFormBaseuseFormBase.jsFormBase flattening and field iteration
useFormBaseLogicuseFormBaseLogic.jsFormBase rendering logic
useFormatteruseFormatter.jsTable column value formatters
useImageuseImage.jsImage media-library input state
useInertiaRequestsuseInertiaRequests.jsInertia.js in-flight request state
useInputuseInput.jsBase input state, modelValue, schema binding
useInputFetchuseInputFetch.jsPaginated remote data fetch for select inputs
useInputHandlersuseInputHandlers.jsSlot-driven input click handlers
useItemActionsuseItemActions.jsForm action buttons (request / modal / download / blank)
useLocaleuseLocale.jsActive locale helpers
useMediaItemsuseMediaItems.jsSelected media item list management
useMediaLibraryuseMediaLibrary.jsOpen/close media library modal
useModaluseModal.jsModal open/close, width, fullscreen state
useModelValueuseModelValue.jsv-model two-way binding helper
useModuleuseModule.jsModule name translation and metadata
useNavigationLayoutuseNavigationLayout.jsTopbar / bottom-nav config merging
useRandKeyuseRandKey.jsUnique component instance key
useRepeateruseRepeater.jsRepeater block state, add / delete / duplicate
useRootuseRoot.jsVuetify / root instance access
useSidebaruseSidebar.jsSidebar open/close, rail, resize
useSvguseSvg.jsSVG symbol existence and locale lookup
useTableuseTable.jsMain data-table composable
useUseruseUser.jsAuthenticated user state and authorization proxy
useValidationuseValidation.jsValidation 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:

js
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
                   useInertiaRequests

Table Sub-hooks ​

useTable is composed from 11 internal sub-hooks. See the Table Sub-hooks Overview for details.

Sub-hookPurpose
useTableActionsToolbar / bulk action props
useTableFiltersSearch, status tabs, advanced filters
useTableFormsCreate/edit form state
useTableGroupClient-side column grouping
useTableHeadersColumn visibility and localStorage
useTableItemEdited item and soft-delete detection
useTableItemActionsPer-row action dispatch
useTableIteratorIterator (card/list) layout actions
useTableModalsDelete / custom / show modals
useTableNamesi18n titles and dialog text
useTableStateURL/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-hookPurpose
useBadgeBadge visibility and props for action buttons
useGenerateButton prop generation with Inertia-aware href handling
usePaginationInfinite-scroll / load-more pagination state
useSelectSelect input prop definitions (makeSelectProps)