CWA 2.0 is in alpha: try it, build with it, give feedback. APIs can still change between releases; see Releases.
Form
useCwaForm
Handle form submission, track in-flight state, and read form-level errors from a CWA Form component resource.
useCwaForm manages the submission lifecycle for a Form component. It reads the current field values collected by useCwaFormInput, submits them to the API, and tracks success and error state reactively.
const { submit, submitting, success, formErrors, unregisteredFieldErrors } = useCwaForm(iri)
Signature
useCwaForm(iri: Ref<string | undefined>)
Parameters
| Parameter | Type | Description |
|---|---|---|
iri | Ref<string | undefined> | IRI of the Form component resource |
Return Values
| Property | Type | Description |
|---|---|---|
submit | () => Promise<void> | Submit the form — see below |
submitting | Ref<boolean> | true while the submit request is in-flight |
success | Ref<boolean> | true after a successful submission |
formErrors | ComputedRef<string[]> | Root form-level error messages from the API response |
unregisteredFieldErrors | ComputedRef<string[]> | Errors from fields the API returned but no useCwaFormInput is bound to — prevents silent error loss |
submit()
Calling submit():
- Resets
successtofalse - Reads the current field values for this form from the shared store (populated by each
useCwaFormInputfield) - Reads
actionandmethodfrom the root form vars in the API response - POSTs or PATCHes to the submit endpoint with all field values using Symfony
full_namekeys (e.g.contact_form[name]) - On success — sets
success = trueand clearssubmitAttempted(hides errors in all fields) - On failure (422) — sets
submitAttempted = true(triggersdisplayErrorsin everyuseCwaFormInputfor this form);formErrorsandunregisteredFieldErrorsupdate automatically from the store
submitting is true for the duration of the request.
Error Display Contract
Three error sources cover every possible API error:
| Source | What it contains | Where to render |
|---|---|---|
useCwaFormInput.errors, gated by displayErrors | Errors for that specific registered field | Next to the field |
formErrors | Root-level form messages (e.g. CSRF, global validation) | At the top of the form |
unregisteredFieldErrors | Errors for form fields the API returned but no useCwaFormInput bound | Next to formErrors |
Always render unregisteredFieldErrors alongside formErrors so API errors are never silently swallowed — the composable already excludes fields that are registered, so there is no duplication.
Example
<script setup lang="ts">
import type { IriProp } from '#cwa/composables/cwa-resource'
const props = defineProps<IriProp>()
const { exposeMeta } = useCwaComponent(props)
defineExpose(exposeMeta)
const iriRef = toRef(props, 'iri')
const { submit, submitting, success, formErrors, unregisteredFieldErrors } = useCwaForm(iriRef)
</script>
<template>
<form @submit.prevent="submit">
<!-- fields rendered by child components using useCwaFormInput -->
<p v-for="error in formErrors" :key="error" class="text-red-500">{{ error }}</p>
<p v-for="error in unregisteredFieldErrors" :key="error" class="text-orange-500">{{ error }}</p>
<button type="submit" :disabled="submitting">
{{ submitting ? 'Sending…' : 'Submit' }}
</button>
<p v-if="success">Thank you — your message has been sent.</p>
</form>
</template>
Notes
formErrorsreads the root form'svars.errorsfrom the store — it updates automatically on 422unregisteredFieldErrorsiterates all form field views in the store, skipping the root and any key registered viauseCwaFormInput; useful as a fallback block- The
submitAttemptedflag is shared state in$cwa.formskeyed by IRI — noprovide/injectrequired; alluseCwaFormInputinstances for the same form IRI respond to it automatically - See the Forms guide for full field-type examples