$form()
$form is the core composable for managing form-level state, validation, and submission. It automatically integrates with fields and provides a complete form management solution.
Creates and manages a complete form instance with typed validation. Handles form-wide operations like validation, submission, and state management.
<template>
<arkform name="login" :submit="handleSubmit">
<field name="email" />
<field name="password" />
</arkform>
</template>
<script setup>
const { $form } = defineArkform({
login: {
schema: arktype({
email: "string.email",
password: "string"
})
}
})
const form = $form("login")
const handleSubmit = form.handleSubmit(async (result) => {
// Handle form submission
})
</script>✅ Signature
$form<FormName>(formName: FormName): {
state: Ref<ArkFormState<FormSchema>>
loading: Ref<boolean>
errors: Ref<string[]>
messages: Ref<string[]>
isLoading: ComputedRef<boolean>
mode: ComputedRef<ArkMode>
input: ComputedRef<FormType>
set(data: Partial<FormType>): Result<Partial<FormType>>
validate(): Result<FormType, string[]>
clear(thingsToClear?: ("input" | "errors" | "messages")[]): Result<string>
handleSubmit(callback: (data: Result<FormType, string[]>) => void): () => Promise<Result<null, string>>
mount(input: Ref<ArkInputState>): void
}📥 Parameters
| Name | Type | Description |
|---|---|---|
formName | string | Form identifier that must match a form defined in defineArkform |
The form schema is automatically inferred from the defineArkform configuration.
🧠 Internal State
The form instance uses useState with keys:
arkform:form:${formName} // Main form state
arkform:form:${formName}:errors // Form-level errors
arkform:form:${formName}:messages // Form-level messages
arkform:form:${formName}:loading // Loading stateThis provides shared access across components and persists through HMR.
🧩 Returned API
state: Ref<ArkFormState>
The complete reactive form state containing:
name: Form nameid: Unique form identifiermode: Current form modeinputs: Record of all mounted field refs
loading: Ref<boolean>
Loading state for form operations, automatically managed by handleSubmit.
errors: Ref<string[]>
Form-level error messages (separate from field-level errors).
messages: Ref<string[]>
Form-level success or info messages.
isLoading: ComputedRef<boolean>
Computed loading state (same as loading.value).
mode: ComputedRef<ArkMode>
Reactive form mode:
"error"if any errors exist"success"if any messages exist"idle"if neither
input: ComputedRef<FormType>
Flattened object of all current field values, strongly typed based on schema:
// For login form with email/password
form.input.value // { email: string, password: string }set(data: Partial<FormType>): Result<Partial<FormType>>
Sets multiple field values at once with validation:
form.set({ email: "user@example.com", password: "secret123" })validate(): Result<FormType, string[]>
Validates all form fields and returns:
ok(formData)if all fields are validerr(errorMessages)if validation fails
clear(thingsToClear?: ("input" | "errors" | "messages")[])
Clears form state across all fields:
- Default: clears input, errors, and messages
- Can specify subset:
form.clear(["errors"])
handleSubmit(callback: (data: Result<FormType, string[]>) => void)
Creates a submission handler that:
- Automatically manages loading state
- Validates the form
- Calls your callback with the result
- Handles errors gracefully
const submit = form.handleSubmit(async (result) => {
if (!result.ok) {
// Handle validation errors
return
}
// Submit validated data
await api.submitForm(result.data)
})mount(input: Ref<ArkInputState>)
Registers a field with the form (called automatically by $field).
🔁 Complete Usage Example
// 1. Define forms
const { $form, $field } = defineArkform({
contact: {
schema: arktype({
name: "string",
email: "string.email",
message: "string"
})
}
})
// 2. Create form instance
const form = $form("contact")
// 3. Access form data
console.log(form.input.value) // { name: "", email: "", message: "" }
console.log(form.mode.value) // "idle" | "error" | "success"
// 4. Set values
form.set({ name: "John", email: "john@example.com" })
// 5. Validate
const result = form.validate()
if (result.ok) {
console.log("Valid form data:", result.data)
}
// 6. Submit with loading state
const handleSubmit = form.handleSubmit(async (result) => {
if (!result.ok) {
form.errors.value = result.message
return
}
await submitToAPI(result.data)
form.messages.value = ["Form submitted successfully!"]
})Component Integration
<template>
<arkform name="contact" theme="default" :submit="handleSubmit">
<field name="name" />
<field name="email" />
<field name="message" is="textarea" />
<div v-if="form.mode.value === 'error'" class="errors">
<p v-for="error in form.errors.value">{{ error }}</p>
</div>
<button type="submit" :disabled="form.isLoading.value">
{{ form.isLoading.value ? 'Submitting...' : 'Submit' }}
</button>
</arkform>
</template>🧠 Notes
- Forms automatically discover and mount fields placed inside
<arkform>components - The
handleSubmitpattern ensures consistent loading states and error handling - Form state persists across component remounts and HMR
- All operations return
Resulttypes for consistent error handling - Type safety is enforced throughout - you can only access fields that exist in your schema