rForm
A flexible form wrapper component that leverages VeeValidate's useForm composable and supports schema-based validation using Zod.
Props
| Name | Type | Required | Description |
|---|---|---|---|
validationSchema | ZodSchema<T> | ✅ | A Zod schema used to validate the form. |
initialValues | Partial<T> | ❌ | Optional initial values for the form fields. |
handleSubmit | (values: GenericObject) => void | ✅ | Callback function to handle the validated form data on submission. |
ui | Partial<ComponentSlots<typeof rFormStyles.slots>> | ❌ | Custom class overrides for styling individual form sections. |
Slots
| Name | Description | Slot Props |
|---|---|---|
header | Rendered at the top of the form. | – |
default | Main form content (e.g., inputs). | – |
cta | Call-to-action area (e.g., submit btn). | isSubmitting |
footer | Additional content under the form. | – |
Emits
This component does not emit any custom events. Form submission is handled via the handleSubmit prop.
Code snippets
Example usage of rForm with rInput. Let's take for example login form:
- First we create our validation schema with
Zod - Then we create our form using
rForm
ts
import { z } from "zod";
export const signInFormSchema = z.object({
email: z
.string()
.min(1, { message: "E-mail je povinný" })
.email({ message: "Nesprávny formát emailu" }),
password: z.string().min(1, { message: "Heslo je povinné" }),
});
export type TSignInFormSchema = z.infer<typeof signInFormSchema>;vue
<script setup lang="ts">
// Import validation schema "formSchema.ts"
import { signInFormSchema, type TSignInFormSchema } from "./formSchema";
import type { GenericObject } from "vee-validate";
// Get form instace to call `validate()` in submitHandler
const signInForm = useTemplateRef("signInForm");
// Define initial form state [Optional]
const initialFormState = {
email: "",
password: "",
} satisfies TSignInFormSchema;
// Submit handler
const onSubmit = async (values: GenericObject) => {
try {
await signInForm.value?.form.validate();
} catch (error) {
console.log(error);
}
};
</script>
<template>
<RForm
ref="signInForm"
class="c-sign-in-form"
:validation-schema="signInFormSchema"
:handle-submit="onSubmit"
:initial-values="initialFormState"
>
<template #header>
<h2 class="c-sign-in-form__title">Prihlásenie</h2>
</template>
// Form inputs
<div class="c-sign-in-form__inputs">
<RInput id="signInEmail" name="email" label="E-mail" type="email" />
<RInput
id="signInPassword"
name="password"
label="Heslo"
type="password"
/>
</div>
<template #cta="{ isSubmitting }">
<RButton :is-loading="isSubmitting.value">
Sign In
</RButton>
</template>
<template #footer>
/* Any additional info */
</template>
</RForm>
</template>⚠️ Value of rInput's name attribute
- For validation to work, the
nameattribute ofrInputhas to be the same as in your validation shcema
What about nested fields?
- Let's look at case when you have fields nested in object. We will use the example above and update it a little bit:
ts
export const signInFormSchema = z.object({
accountDetails: z.object({
email: z
.string()
.min(1, { message: "E-mail je povinný" })
.email({ message: "Nesprávny formát emailu" }),
}),
password: z.string().min(1, { message: "Heslo je povinné" }),
});vue
<!-- `rInput` usage with new structure of validation schema -->
<RInput id="signInEmail" name="accountDetails.email" label="E-mail" type="email" />