Api
useAction
API reference for the useAction Vue composable — reactive execution, status tracking, validation errors, and lifecycle callbacks for type-safe server actions.
useAction
Vue composable for executing server actions with reactive state.
Import
import { useAction } from '#imports' // auto-imported
Signature
function useAction<TInput, TOutput>(
action: SafeAction<TInput, TOutput>,
callbacks?: ActionCallbacks<TInput, TOutput>
): UseActionReturn<TInput, TOutput>
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
action | SafeAction | Yes | An action imported from #safe-action/actions |
callbacks | ActionCallbacks | No | Lifecycle callbacks |
Return Values
| Property | Type | Description |
|---|---|---|
execute(input) | (input: TInput) => void | Fire-and-forget execution. Triggers callbacks but does not return a value. |
executeAsync(input) | (input: TInput) => Promise<ActionResult> | Awaitable execution. Returns the full action result. |
data | Ref<TOutput | undefined> | The success data from the last execution. |
serverError | Ref<string | undefined> | Server error message from the last execution. |
validationErrors | Ref<Record<string, string[]> | undefined> | Per-field validation errors from Zod or returnValidationErrors. |
status | Ref<ActionStatus> | Current status: 'idle', 'executing', 'hasSucceeded', or 'hasErrored'. |
isIdle | ComputedRef<boolean> | true when status is 'idle'. |
isExecuting | ComputedRef<boolean> | true when status is 'executing'. |
hasSucceeded | ComputedRef<boolean> | true when status is 'hasSucceeded'. |
hasErrored | ComputedRef<boolean> | true when status is 'hasErrored'. |
reset() | () => void | Reset all state (data, serverError, validationErrors, status) to initial values. |
Callbacks
| Callback | Signature | Description |
|---|---|---|
onSuccess | ({ data, input }) => void | Called when the action succeeds. data is the typed return value. |
onError | ({ error, input }) => void | Called on server or validation error. |
onSettled | ({ result, input }) => void | Called after every execution, regardless of outcome. |
onExecute | ({ input }) => void | Called when execution starts, before the request is made. |
Examples
Basic
<script setup lang="ts">
import { greet } from '#safe-action/actions'
const { execute, data, isExecuting } = useAction(greet)
</script>
With All Callbacks
<script setup lang="ts">
import { createPost } from '#safe-action/actions'
const { execute, data, validationErrors, serverError } = useAction(createPost, {
onExecute({ input }) {
console.log('Starting with input:', input)
},
onSuccess({ data, input }) {
console.log('Created:', data.title)
navigateTo(`/posts/${data.id}`)
},
onError({ error, input }) {
console.error('Failed to create post:', error)
},
onSettled({ result, input }) {
console.log('Finished, result:', result)
},
})
</script>
Async Execution
<script setup lang="ts">
import { createPost } from '#safe-action/actions'
const { executeAsync } = useAction(createPost)
async function handleSubmit(formData: { title: string; body: string }) {
const result = await executeAsync(formData)
if (result.data) {
await navigateTo(`/posts/${result.data.id}`)
}
if (result.serverError) {
showToast(result.serverError)
}
}
</script>
Form with Validation Errors
<script setup lang="ts">
import { register } from '#safe-action/actions'
const form = reactive({ email: '', password: '' })
const { execute, validationErrors, serverError, isExecuting } = useAction(register, {
onSuccess() {
navigateTo('/dashboard')
},
})
</script>
<template>
<form @submit.prevent="execute(form)">
<div>
<label>Email</label>
<input v-model="form.email" type="email" />
<p v-if="validationErrors?.email" class="error">
{{ validationErrors.email[0] }}
</p>
</div>
<div>
<label>Password</label>
<input v-model="form.password" type="password" />
<p v-if="validationErrors?.password" class="error">
{{ validationErrors.password[0] }}
</p>
</div>
<p v-if="serverError" class="error">{{ serverError }}</p>
<button type="submit" :disabled="isExecuting">
{{ isExecuting ? 'Registering...' : 'Register' }}
</button>
</form>
</template>