Type-safe wrapper for async operations with loading state:
const isSubmitting = ref(false)
const withLoading = createWithLoading(isSubmitting)
await withLoading(async () => {
await authClient.sendVerificationEmail({ email })
})
Location: /app/utils/withLoading.ts
Preserves function signatures while adding loading state:
const isLoading = ref(false)
const fetchUserWithLoading = createWithLoadingFor(isLoading, authClient.getUser)
// Maintains original args and return type
const user = await fetchUserWithLoading('123', options)
Composable wrapper combining both patterns:
const { isLoading, fnWithLoading: sendVerificationEmail } = useWithLoading(_sendVerificationEmail)
await sendVerificationEmail('ada@example.com')
Location: /app/composables/useWithLoading.ts
Strongly typed notification system with preset colors/icons:
const { errorToast, successToast, infoToast } = useToastMessage()
successToast({
title: 'Profile updated',
description: 'Your changes have been saved',
})
errorToast({
title: 'Update failed',
description: error.message || 'Unable to update. Please try again.',
})
Location: /app/composables/useToastMessage.ts
Cookie-based pattern for server-to-client notifications:
// Server sets cookie
event.node.res.setHeader('Set-Cookie', serialize('flash_message__success', 'Account created!'))
// Client displays on mount
showFlashMessageFromCookie() // Auto-called in app.vue
Cookies: flash_message__error, flash_message__info, flash_message__success
Location: /app/utils/showFlashMessageFromCookie.ts
Standard pattern combining loading, validation, and error handling:
<script lang="ts" setup>
import type { FormSubmitEvent } from '@nuxt/ui'
import type * as z from 'zod'
// Define form schema and state
const schema = z.object({ /* ... */ })
type Schema = z.output<typeof schema>
const state = reactive<Schema>({ /* ... */ })
// Separate handler for wrapping with useWithLoading
async function _onSubmit(event: FormSubmitEvent<Schema>) {}
// Wrap handler with loading state - provides isLoading ref + wrapped fn
const { isLoading, fnWithLoading: onSubmit } = useWithLoading(_onSubmit)
</script>
<template>
<UForm
:schema="schema"
:state="state"
class="space-y-6"
@submit="onSubmit"
>
<!-- Form Fields -->
<!-- Submit Button -->
<UButton
type="submit"
size="lg"
block
:loading="isLoading"
>
Add User
</UButton>
</UForm>
</template>
Location: /app/components/page/admin/users/CreateUserModal.vue
Composable for confirmation dialogs:
const { confirm } = useConfirm({
title: 'Deactivate Account?',
body: 'Your account will be deactivated immediately. This action cannot be undone.',
confirmBtnProps: { label: 'Deactivate', color: 'error' },
onConfirm: async () => {
await userStore.deactivateCurrentAccount()
},
})
await confirm() // Non-dismissible during async operation
Location: /app/composables/useConfirm.ts