Install
$ agentstack add skill-bbeierle12-skill-mcp-claude-form-vue ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Form Vue
Production Vue 3 form patterns. Default stack: VeeValidate + Zod.
Quick Start
npm install vee-validate @vee-validate/zod zod
import { useForm, useField } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/zod';
import { z } from 'zod';
// 1. Define schema
const schema = toTypedSchema(z.object({
email: z.string().email('Invalid email'),
password: z.string().min(8, 'Min 8 characters')
}));
// 2. Use form
const { handleSubmit, errors } = useForm({ validationSchema: schema });
const { value: email } = useField('email');
const { value: password } = useField('password');
// 3. Handle submit
const onSubmit = handleSubmit((values) => {
console.log(values);
});
{{ errors.email }}
{{ errors.password }}
Sign in
When to Use Which
| Criteria | VeeValidate | Vuelidate | |----------|-------------|-----------| | API Style | Declarative (schema) | Imperative (rules) | | Zod Integration | ✅ Native adapter | Manual | | Bundle Size | ~15KB | ~10KB | | Component Support | ✅ Built-in Field/Form | Manual binding | | Async Validation | ✅ Built-in | ✅ Built-in | | Cross-field Validation | ✅ Easy | More manual | | Learning Curve | Low | Medium |
Default: VeeValidate — Better DX, native Zod support.
Use Vuelidate when:
- Need extremely fine-grained control
- Existing Vuelidate codebase
- Prefer imperative validation style
VeeValidate Patterns
Basic Form with Composition API
import { useForm, useField } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/zod';
import { loginSchema, type LoginFormData } from './schemas';
const emit = defineEmits();
// Form setup
const { handleSubmit, errors, meta } = useForm({
validationSchema: toTypedSchema(loginSchema),
validateOnMount: false
});
// Field setup
const { value: email, errorMessage: emailError, meta: emailMeta } = useField('email');
const { value: password, errorMessage: passwordError, meta: passwordMeta } = useField('password');
const { value: rememberMe } = useField('rememberMe');
// Submit handler
const onSubmit = handleSubmit((values) => {
emit('submit', values);
});
Email
{{ emailError }}
Password
{{ passwordError }}
Remember me
{{ meta.pending ? 'Signing in...' : 'Sign in' }}
Reusable FormField Component
import { useField } from 'vee-validate';
import { computed, useId } from 'vue';
interface Props {
name: string;
label: string;
type?: string;
autocomplete?: string;
hint?: string;
required?: boolean;
}
const props = withDefaults(defineProps(), {
type: 'text'
});
const fieldId = useId();
const errorId = `${fieldId}-error`;
const hintId = `${fieldId}-hint`;
const { value, errorMessage, meta } = useField(() => props.name);
const showError = computed(() => meta.touched && !!errorMessage.value);
const showValid = computed(() => meta.touched && !errorMessage.value && meta.valid);
const describedBy = computed(() => {
const ids = [];
if (props.hint) ids.push(hintId);
if (showError.value) ids.push(errorId);
return ids.length > 0 ? ids.join(' ') : undefined;
});
{{ label }}
*
{{ hint }}
✓
✗
{{ errorMessage }}
Using FormField Component
import { useForm } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/zod';
import { loginSchema } from './schemas';
import FormField from './FormField.vue';
const { handleSubmit, meta } = useForm({
validationSchema: toTypedSchema(loginSchema)
});
const onSubmit = handleSubmit((values) => {
console.log(values);
});
Sign in
Form with Initial Values
import { useForm } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/zod';
import { profileSchema } from './schemas';
interface Props {
initialData?: {
firstName: string;
lastName: string;
email: string;
}
}
const props = defineProps();
const { handleSubmit, resetForm } = useForm({
validationSchema: toTypedSchema(profileSchema),
initialValues: props.initialData
});
// Reset to initial values
const handleCancel = () => {
resetForm();
};
// Reset to new values
const handleReset = (newValues: typeof props.initialData) => {
resetForm({ values: newValues });
};
Async Validation (Username Check)
import { useField } from 'vee-validate';
import { z } from 'zod';
import { toTypedSchema } from '@vee-validate/zod';
// Schema with async validation
const usernameSchema = z.string()
.min(3, 'Username must be at least 3 characters')
.refine(async (username) => {
const response = await fetch(`/api/check-username?u=${username}`);
const { available } = await response.json();
return available;
}, 'Username is already taken');
const { value, errorMessage, meta } = useField('username', toTypedSchema(usernameSchema));
Username
Checking...
{{ errorMessage }}
Cross-Field Validation (Password Confirmation)
import { useForm, useField } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/zod';
import { z } from 'zod';
const schema = toTypedSchema(
z.object({
password: z.string().min(8, 'Min 8 characters'),
confirmPassword: z.string()
}).refine(data => data.password === data.confirmPassword, {
message: 'Passwords do not match',
path: ['confirmPassword']
})
);
const { handleSubmit } = useForm({ validationSchema: schema });
const { value: password } = useField('password');
const { value: confirmPassword, errorMessage: confirmError } = useField('confirmPassword');
{{ confirmError }}
Field Arrays (Dynamic Fields)
import { useForm, useFieldArray } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/zod';
import { z } from 'zod';
const schema = toTypedSchema(z.object({
teammates: z.array(z.object({
name: z.string().min(1, 'Name required'),
email: z.string().email('Invalid email')
})).min(1, 'Add at least one teammate')
}));
const { handleSubmit } = useForm({
validationSchema: schema,
initialValues: {
teammates: [{ name: '', email: '' }]
}
});
const { fields, push, remove } = useFieldArray('teammates');
1">
Remove
Add teammate
Submit
Vuelidate Patterns
Basic Form
import { reactive, computed } from 'vue';
import { useVuelidate } from '@vuelidate/core';
import { required, email, minLength } from '@vuelidate/validators';
const state = reactive({
email: '',
password: ''
});
const rules = computed(() => ({
email: { required, email },
password: { required, minLength: minLength(8) }
}));
const v$ = useVuelidate(rules, state);
const onSubmit = async () => {
const isValid = await v$.value.$validate();
if (!isValid) return;
console.log('Submitting:', state);
};
Email
{{ v$.email.$errors[0]?.$message }}
Password
{{ v$.password.$errors[0]?.$message }}
Sign in
Vuelidate with Zod
import { reactive } from 'vue';
import { useVuelidate } from '@vuelidate/core';
import { helpers } from '@vuelidate/validators';
import { z } from 'zod';
// Create Vuelidate validator from Zod schema
function zodValidator(schema: T) {
return helpers.withMessage(
(value: unknown) => {
const result = schema.safeParse(value);
if (!result.success) {
return result.error.errors[0]?.message || 'Invalid';
}
return true;
},
(value: unknown) => {
const result = schema.safeParse(value);
return result.success;
}
);
}
const emailSchema = z.string().email('Please enter a valid email');
const passwordSchema = z.string().min(8, 'Password must be at least 8 characters');
const state = reactive({
email: '',
password: ''
});
const rules = {
email: { zodValidator: zodValidator(emailSchema) },
password: { zodValidator: zodValidator(passwordSchema) }
};
const v$ = useVuelidate(rules, state);
Shared Zod Schemas
// schemas/index.ts (shared between React and Vue)
import { z } from 'zod';
export const loginSchema = z.object({
email: z.string().min(1, 'Email is required').email('Invalid email'),
password: z.string().min(1, 'Password is required'),
rememberMe: z.boolean().optional().default(false)
});
export type LoginFormData = z.infer;
// VeeValidate usage
import { toTypedSchema } from '@vee-validate/zod';
const veeSchema = toTypedSchema(loginSchema);
// React Hook Form usage
import { zodResolver } from '@hookform/resolvers/zod';
const rhfResolver = zodResolver(loginSchema);
File Structure
form-vue/
├── SKILL.md
├── references/
│ ├── veevalidate-patterns.md # VeeValidate deep-dive
│ └── vuelidate-patterns.md # Vuelidate deep-dive
└── scripts/
├── veevalidate-form.vue # VeeValidate patterns
├── vuelidate-form.vue # Vuelidate patterns
├── form-field.vue # Reusable field component
└── schemas/ # Shared with form-validation
├── auth.ts
├── profile.ts
└── payment.ts
Reference
references/veevalidate-patterns.md— Complete VeeValidate patternsreferences/vuelidate-patterns.md— Vuelidate patterns
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Bbeierle12
- Source: Bbeierle12/Skill-MCP-Claude
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.