--- name: sdlc-vue-forms description: | Vue 3 form patterns: native v-model, vee-validate + zod (most common), VueUse helpers, defineModel for custom inputs, controlled vs uncontrolled, field arrays, multi-step wizards. Use this skill to: - Wire vee-validate with a validation schema (zod / yup). - Build custom form components with defineModel (Vue 3.4+). - Implement multi-step forms. - Handle async validation. - Integrate forms with TanStack Query mutations or Pinia actions. Do NOT use this skill for: - General SFC conventions (see vue-conventions). - State management (see vue-state-management). - Routing (see vue-routing). - Testing forms (see vue-testing). paths: ["src/**/*.vue"] --- # Vue 3 Form Patterns `v-model` is the foundation. Add vee-validate when validation grows beyond simple HTML5 attributes. ## Detection | Marker (in deps) | Library | |---|---| | `vee-validate` (+ `@vee-validate/zod` or `@vee-validate/yup`) | vee-validate (recommended) | | `@vueuse/core` | VueUse helpers (lighter alternative) | | (none) | Native v-model + HTML5 validation | | `zod` / `yup` / `valibot` | Pair with vee-validate via resolver | ## Native v-model (sufficient for simple forms) ```vue ``` Built-in HTML5 validation (`required`, `type="email"`, `pattern`, `minlength`, `maxlength`) is free. Use it for forms ≤3 fields with simple rules. ### v-model modifiers ```vue ``` ### v-model on custom components In Vue 3.4+, use `defineModel`: ```vue ``` ```vue ``` For Vue 3.0–3.3, use manual props + emits: ```vue ``` ## vee-validate (recommended for non-trivial forms) ```bash pnpm add vee-validate @vee-validate/zod zod ``` ### Basic form ```vue ``` ### Field components alternative ```vue ``` `` and `` are convenient but less flexible. Pick one approach per project. ### Field arrays ```vue ``` `field.key` is a stable key from vee-validate — DON'T use index. ### Async validation ```ts const schema = z.object({ username: z.string().min(3).refine( async (u) => { const res = await fetch(`/api/users/check?u=${u}`); return (await res.json()).available; }, 'Username taken' ), }); const { defineField } = useForm({ validationSchema: toTypedSchema(schema), validateOnInput: false, validateOnBlur: true, // validate on blur — appropriate for expensive checks }); ``` ### Server error handling ```ts const { handleSubmit, setErrors, setFieldError } = useForm({...}); const onSubmit = handleSubmit(async (values) => { try { await createUser(values); } catch (err) { if (err instanceof FetchError && err.status === 409) { setFieldError('email', 'Email already in use'); } else { setErrors({ form: 'Something went wrong' }); } } }); ``` ### Multi-step forms ```vue ``` ## VueUse useForm (lighter alternative) `@vueuse/core` doesn't have a direct `useForm` equivalent. For simple forms, native v-model + manual validation is enough. For complex forms, use vee-validate. VueUse useful form-related composables: - `useDebouncedRef` — debounce input value. - `useStorage` — persist form state to localStorage (NEVER for sensitive data). - `onClickOutside` — close dropdowns / pickers. - `useFocus` — focus management for accessibility. ## Integration with TanStack Query / Pinia ```vue ``` ## Accessibility checklist - Every input has a `