跳到正文
cccskills
免费在 GitHub 公开

web-forms-vee-validate

VeeValidate v4 patterns - useForm, useField, defineField, useFieldArray, schema validation with Composition API

中文简介正在准备中。当前显示原文说明。

查看安装方法

包含的文件(6)

  • SKILL.md11.0 KB
  • examples/arrays.md15.0 KB
  • examples/core.md11.9 KB
  • examples/validation.md9.0 KB
  • metadata.yaml367 B
  • reference.md12.7 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

VeeValidate Form Validation Patterns

Quick Guide: useForm owns the form state; defineField returns a [model, attrs] tuple to v-model onto a native input, and useField binds a field inside a reusable input component — with its name passed as a getter so it stays reactive. A schema reaches validationSchema through toTypedSchema(), which is also what supplies the types. useFieldArray drives repeatable groups and iterates on field.key. Everything here is v4; v5 removes the adapter, so check reference.md before targeting it.

Detailed Resources:

  • examples/core.md — defineField, inline rules, a reusable input built on useField, form meta, eager validation
  • examples/validation.md — schema through toTypedSchema, conditional schemas, all errors per field
  • examples/arrays.md — useFieldArray, nested arrays, reordering
  • reference.md — return-value tables, composition helpers, worked anti-patterns, v5 migration

Which path applies

  • The form is built in the component around native inputs — useForm plus defineField, and v-model on the returned model. Follow examples/core.md.
  • A reusable input component — useField inside the component, taking its name as () => props.name. Follow examples/core.md Pattern 3.
  • The renderless <Form> and <Field> components — an alternative to the Composition API rather than a companion to it. Each creates its own form context, so a component that calls useForm and also renders <Form> has two, and the fields register with whichever they are nested in.

<critical_requirements>

Before writing VeeValidate code

Wrap a schema in toTypedSchema() before handing it to validationSchema. The adapter converts the schema's result into the shape the form reads and is what supplies the value types; a raw schema is accepted without complaint and then validates nothing.

Pass useField its name as () => props.name or toRef(props, "name"). A plain props.name is read once at setup, so the field keeps binding to the name the component first mounted with.

Give initialValues an entry for every field, arrays included. Field arrays iterate their value and fail on undefined, and a cross-field rule is skipped entirely when one of the keys it compares is missing.

Iterate useFieldArray on field.key. It is the identity VeeValidate assigns each entry and it survives insertion and reordering, which a positional index does not.

</critical_requirements>


Auto-detection: vee-validate, @vee-validate/zod, @vee-validate/yup, @vee-validate/valibot, useForm, useField, defineField, useFieldArray, toTypedSchema, validationSchema, errorBag, handleSubmit, resetForm, setFieldError, setErrors, validateOnValueUpdate, keepValuesOnUnmount, ErrorMessage, useFormContext

Applies to:

  • Vue form state, validation timing and submission
  • Reusable input components that bind themselves to a named field
  • Repeatable field groups that add, remove and reorder
  • Surfacing server-side validation failures against individual fields
  • Multi-step forms where fields unmount between steps

Handled elsewhere:

  • Authoring the validation schema — toTypedSchema adapts whatever schema it is given, and how that schema states its rules is settled by whatever owns it.
  • Choosing a schema library — an adapter package exists for each, and the choice sits outside this skill.
  • Markup and styling of the inputs — every example uses plain elements, so the classes are yours.
  • Where the initial values came from — initialValues is a plain object the form does not fetch.

<philosophy>

Validation is declared once, at the form, and read per field. useForm holds the schema and the values; each field asks the form for its own slice, so a field knows its error without knowing the rule that produced it. That is what lets an input component be written without knowing which form it will be dropped into — it takes a name and binds itself.

Reactivity is the thing to keep intact. Everything a field receives from the form is a ref or a getter, and the common failure is flattening one of them: reading props.name instead of passing a getter, or unwrapping a computed schema with .value before the form can track it.

</philosophy>

<decision_framework>

defineField or useField

defineFielduseField
Returns[model, attrs] for v-model and v-bindvalue, errorMessage, meta, handlers
Form contextRequired — it comes off a useForm resultOptional; falls back to standalone validation
Best forThe form's own template, native inputsA component that binds itself by name

defineField where the form and the inputs are in one component. useField where the input is a component in its own right, or where the binding needs handlers rather than a v-model.

</decision_framework>


<patterns>

Core patterns

Pattern 1: useForm with defineField

defineField returns a tuple: the model for v-model, and the attrs carrying the event handlers that drive validation timing.

<script setup lang="ts">
const { handleSubmit, errors, defineField } = useForm({
  validationSchema: schema,
  initialValues: { email: "", password: "" },
});

const [email, emailAttrs] = defineField("email");

const onSubmit = handleSubmit(async (values) => {
  await login(values);
});
</script>

<template>
  <input v-model="email" v-bind="emailAttrs" />
  <span v-if="errors.email" role="alert">{{ errors.email }}</span>
</template>

Dropping v-bind="emailAttrs" leaves the model bound but the handlers unattached, so the field never blurs and never validates.

Full code: examples/core.md Pattern 1


Pattern 2: useField in a reusable input

The name arrives as a getter so the field re-binds if the prop changes.

<script setup lang="ts">
const props = defineProps<{ name: string }>();

const { value, errorMessage, handleBlur, handleChange, meta } =
  useField<string>(() => props.name, undefined, {
    validateOnValueUpdate: false,
  });
</script>

validateOnValueUpdate: false holds validation back to blur, which is what stops errors appearing while the user is still typing the first character.

Full code: examples/core.md Pattern 3


Pattern 3: Schema through toTypedSchema

The adapter is the boundary: the schema states the rules, and toTypedSchema renders them as VeeValidate errors and as the type of values.

import { toTypedSchema } from "@vee-validate/zod";

const schema = toTypedSchema(registrationSchema);

const { handleSubmit, errors } = useForm({
  validationSchema: schema,
  initialValues: { email: "", password: "", confirmPassword: "" },
});

Every key a cross-field rule compares needs an entry in initialValues — a rule reading a key that is undefined is skipped rather than failed, so the form submits as valid.

Full code: examples/validation.md


Pattern 4: useFieldArray

fields carries a key and a value per entry. The key is the iteration key; the value is what the inputs bind to.

<script setup lang="ts">
const { handleSubmit } = useForm({
  initialValues: { users: [{ name: "", email: "" }] },
});

const { fields, push, remove } = useFieldArray("users");
</script>

<template>
  <div v-for="(field, index) in fields" :key="field.key">
    <input v-model="field.value.name" />
    <button type="button" @click="remove(index)">Remove</button>
  </div>
</template>

Full code: examples/arrays.md


Pattern 5: Server-side errors

setErrors takes a record of field names to messages, and setFieldError sets one. Both put a server's verdict where the field's own error would go, so the template needs no separate branch.

const onSubmit = handleSubmit(async (values) => {
  try {
    await createUser(values);
  } catch (error) {
    const fieldErrors = extractFieldErrors(error);
    if (fieldErrors) {
      setErrors(fieldErrors);
    } else {
      setFieldError("email", "Could not create the account");
    }
  }
});

The next validation run clears them — a server error persists only until the field it names changes, which is why a rejected submission needs the message re-set rather than remembered.


Pattern 6: Form meta

meta aggregates the fields: valid, dirty, touched and pending describe the form as a whole.

<template>
  <button :disabled="!meta.valid || !meta.dirty || isSubmitting">
    {{ isSubmitting ? "Saving..." : "Save" }}
  </button>
  <p v-if="meta.dirty">You have unsaved changes</p>
</template>

After a successful save, resetForm({ values: saved }) makes the saved data the new baseline, which is what returns meta.dirty to false.

Full code: examples/core.md Pattern 4

</patterns>

<red_flags>

Red flags

Breaks at runtime:

  • A schema handed to validationSchema without toTypedSchema() — it is accepted and then never validates, so the form submits whatever it holds.
  • props.name passed to useField instead of a getter — the field binds to the name captured at setup and ignores every later change.
  • initialValues missing a field array — the composable iterates undefined and throws.
  • An array index as the useFieldArray iteration key — Vue matches the wrong entries, so removing a middle row shifts every value below it up one.
  • resetForm(data) in place of resetForm({ values: data }) — the argument shape is wrong, and the reset takes no effect rather than reporting.

Surprising behaviour:

  • errors holds the first error per field; errorBag holds all of them as arrays. A password rule set showing one unmet requirement at a time is reading the wrong one.
  • meta.valid is false on the first render, before validation has run — a submit button gated on it alone starts disabled.
  • keepValuesOnUnmount defaults to false, so a field that unmounts loses its value. Set it true for anything that hides fields between steps.
  • Nested fields address by dot notation (defineField("user.profile.name")) and array items by brackets (errors["items[0].name"]) — the two are not interchangeable.
  • A computed schema must be passed as the computed itself, not .value — unwrapped, the form binds to one snapshot and stops tracking it.
  • validateOnValueUpdate left on validates every keystroke, including the first.
  • Errors shown without checking meta.touched appear before the user has reached the field.

Worked before/after code for the most common of these is in reference.md.

</red_flags>

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints

中文简介正在准备中。当前显示原文说明。

agents-inc/skills252026年10月17日 更新

LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment

中文简介正在准备中。当前显示原文说明。

agents-inc/skills252026年10月17日 更新

Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns

中文简介正在准备中。当前显示原文说明。

agents-inc/skills252026年10月17日 更新

Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint

中文简介正在准备中。当前显示原文说明。

agents-inc/skills252026年10月17日 更新

Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training

中文简介正在准备中。当前显示原文说明。

agents-inc/skills252026年10月17日 更新

Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints

中文简介正在准备中。当前显示原文说明。

agents-inc/skills252026年10月17日 更新

查看 agents-inc 的全部技能

报告此技能的问题