Overview
Installation
Add the shadcn-vue controls you want to compose, then add Stackhacker UI's form layer.
pnpm dlx shadcn-vue@latest add field input textarea select pnpm dlx shadcn-vue@latest add "https://ui.stackhacker.io/r/form.json" pnpm dlx shadcn-vue@latest add "https://ui.stackhacker.io/r/form-field.json"
Usage
<script setup lang="ts">
import { reactive } from 'vue'
import { Form, type FormError } from '@/components/form'
import { FormField } from '@/components/form-field'
import { Input } from '@/components/ui/input'
const state = reactive({ email: '' })
function validate(values: typeof state): FormError[] {
if (!values.email.includes('@')) {
return [{ name: 'email', message: 'Enter a valid email address.' }]
}
return []
}
async function onSubmit(event) {
await saveProfile(event.data)
}
</script>
<template>
<Form :state="state" :validate="validate" @submit="onSubmit">
<FormField v-slot="field" label="Email" name="email">
<Input v-model="state.email" v-bind="field" type="email" />
</FormField>
</Form>
</template>Responsibilities
Form owns the UI protocol: form event handling, validation triggering, error collection, field-level error distribution, and submit / error events.
Your app owns API submission, mutation, persistence, toast, redirect, server error interpretation, auth flows, and business rules.
Regle Integration
Regle remains an app dependency. Stackhacker UI does not import Regle.
<script setup lang="ts">
import { useRegle } from '@regle/core'
import { email, required, withMessage } from '@regle/rules'
import { Form } from '@/components/form'
import { FormField } from '@/components/form-field'
import { Input } from '@/components/ui/input'
const { r$ } = useRegle({ email: '' }, {
email: {
required,
email: withMessage(email, 'Enter a valid email address.')
}
})
</script>
<template>
<Form :schema="r$" :state="r$.$value">
<FormField v-slot="field" label="Email" name="email">
<Input v-model="r$.$value.email" v-bind="field" type="email" />
</FormField>
</Form>
</template>API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
state | unknown | — | App-owned reactive form state. |
schema | unknown | — | Optional schema-like validator such as Regle or Standard Schema. |
validate | (state) => FormError[] | Promise<FormError[]> | — | Custom validation function. |
validateOn | ('input' | 'change' | 'blur')[] | ['blur'] | Input events that trigger validation. |
disabled | boolean | false | Marks the form context disabled for fields. |
class | string | — | Additional CSS classes. |
Events
| Event | Payload | Description |
|---|---|---|
submit | { originalEvent, data } | Emitted after validation succeeds. |
error | { originalEvent, errors, data } | Emitted after validation fails. |
Types
interface FormError {
name: string
message: string
id?: string
}
