Skip to content

Form Components

Form components handle user input, validation, and submission.

Always use PrimeVue components for forms:

ComponentUsage
InputTextText input
InputNumberNumeric input
TextareaMulti-line text
PasswordPassword input
CheckboxBoolean selection
RadioButtonSingle selection
SelectDropdown selection
MultiSelectMultiple selection
AutoCompleteSearch with suggestions
DatePickerDate selection
ColorPickerColor selection
SliderRange selection
ToggleSwitchOn/off toggle

IONFLOW uses VeeValidate with Zod for form validation.

<script setup lang="ts">
import { useForm } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/zod';
import { z } from 'zod';
const schema = toTypedSchema(
z.object({
name: z.string().min(1, 'Name is required'),
email: z.string().email('Invalid email'),
age: z.number().min(18, 'Must be 18 or older'),
})
);
const { handleSubmit, errors, defineField } = useForm({
validationSchema: schema,
});
const [name, nameAttrs] = defineField('name');
const [email, emailAttrs] = defineField('email');
const onSubmit = handleSubmit((values) => {
console.log(values);
});
</script>
<template>
<form @submit="onSubmit">
<div class="field">
<label for="name">Name</label>
<InputText
id="name"
v-model="name"
v-bind="nameAttrs"
:class="{ 'p-invalid': errors.name }"
/>
<small class="p-error">{{ errors.name }}</small>
</div>
<div class="field">
<label for="email">Email</label>
<InputText
id="email"
v-model="email"
v-bind="emailAttrs"
:class="{ 'p-invalid': errors.email }"
/>
<small class="p-error">{{ errors.email }}</small>
</div>
<Button type="submit" label="Submit" />
</form>
</template>

Dynamic form generator based on schema.

Location: src/components/RecursiveForm.vue

<template>
<RecursiveForm
:schema="formSchema"
v-model="formData"
@submit="handleSubmit"
/>
</template>

Drag and drop file upload component.

Location: src/components/FileUploadDragDrop.vue

<template>
<FileUploadDragDrop
accept="image/*"
:max-size="5000000"
@upload="handleUpload"
/>
</template>

Image upload with preview.

Location: src/components/FileUploadImage.vue

<template>
<FileUploadImage
v-model="imageUrl"
:preview="true"
/>
</template>
<template>
<FloatLabel>
<InputText id="username" v-model="username" />
<label for="username">Username</label>
</FloatLabel>
</template>
<template>
<IconField>
<InputIcon class="pi pi-search" />
<InputText v-model="search" placeholder="Search" />
</IconField>
</template>
<template>
<InputGroup>
<InputGroupAddon>
<i class="pi pi-user" />
</InputGroupAddon>
<InputText v-model="username" />
</InputGroup>
</template>
  • Use Zod schemas for validation
  • Show validation errors inline
  • Disable submit during loading
  • Use proper input types
  • Add labels for accessibility
  • Don’t create custom input elements
  • Don’t skip validation
  • Don’t use alert() for errors
  • Don’t block form submission UI