Skip to content

rForm

A flexible form wrapper component that leverages VeeValidate's useForm composable and supports schema-based validation using Zod.

Props

NameTypeRequiredDescription
validationSchemaZodSchema<T>A Zod schema used to validate the form.
initialValuesPartial<T>Optional initial values for the form fields.
handleSubmit(values: GenericObject) => voidCallback function to handle the validated form data on submission.
uiPartial<ComponentSlots<typeof rFormStyles.slots>>Custom class overrides for styling individual form sections.

Slots

NameDescriptionSlot Props
headerRendered at the top of the form.
defaultMain form content (e.g., inputs).
ctaCall-to-action area (e.g., submit btn).isSubmitting
footerAdditional 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:

  1. First we create our validation schema with Zod
  2. 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 name attribute of rInput has 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" />

Made with ♥️ by Riešenia