Meet Frontfriend.Your brand. Your rules. Your agents, connected.Explore what’s new

Form

Building forms with validation and error handling.

Interactive preview

Rendered from the shared Frontfriend stories with React Base, the same preview Studio shows. Styling comes from your Design System; the code below is native to Reka UI.

Installation

Complete project setup first. Your configuration must select vue/reka and include your Design System ID.

pnpm exec frontfriend download form -f vue --primitive reka

Review dependencies reported by the downloader and keep your source changes in version control.

Usage

<script setup lang="ts">
import { Form } from "@/components/ui/form";
import { FormField } from "@/components/ui/form";
import { FormItem } from "@/components/ui/form";
import { FormLabel } from "@/components/ui/form";
import { FormControl } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { FormMessage } from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import * as UI from "@/components/ui/form";

const Component = UI.Form;

const args = {
  "orientation": "vertical",
} satisfies Partial<
  InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown> & { orientation?: "horizontal" | "vertical" };
</script>

<template>
  <Form @submit="() => undefined"
    ><FormField v-slot="{ componentField }" name="email"
      ><FormItem :orientation="args.orientation"
        ><FormLabel>Email</FormLabel
        ><FormControl
          ><Input v-bind="componentField" type="email" /></FormControl
        ><FormMessage /></FormItem></FormField
    ><Button type="submit">Submit</Button></Form
  >
</template>

Examples

Default

<script setup lang="ts">
import { toTypedSchema } from "@vee-validate/zod";
import { z } from "zod";
import { useForm } from "vee-validate";
import { Form } from "@/components/ui/form";
import { FormField } from "@/components/ui/form";
import { FormItem } from "@/components/ui/form";
import { FormLabel } from "@/components/ui/form";
import { FormControl } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { FormMessage } from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import * as UI from "@/components/ui/form";

function useDemoForm() {
  const formSchema = toTypedSchema(
    z.object({
      login: z
        .string()
        .min(2, { message: "Login must be at least 2 characters" }),
      password: z
        .string()
        .min(2, { message: "Password must be at least 2 characters" }),
    }),
  );
  useForm({ validationSchema: formSchema });
  const onSubmit = (values: Record<string, unknown>) => console.log(values);
  return { onSubmit };
}

const Component = UI.Form;

const args = {

} satisfies Partial<
  InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
const { onSubmit } = useDemoForm();
</script>

<template>
  <Form @submit="onSubmit" class="flex w-full flex-col items-start gap-8"
    ><FormField v-slot="{ componentField }" name="login"
      ><FormItem orientation="vertical" class="w-full"
        ><FormLabel>Login</FormLabel
        ><FormControl
          ><Input class="w-full" v-bind="componentField" /></FormControl
        ><FormMessage /></FormItem></FormField
    ><FormField v-slot="{ componentField }" name="password"
      ><FormItem orientation="vertical" class="w-full"
        ><FormLabel>Password</FormLabel
        ><FormControl
          ><Input
            class="w-full"
            type="password"
            v-bind="componentField" /></FormControl
        ><FormMessage /></FormItem></FormField
    ><Button type="submit" class="w-full" variant="main">Submit</Button></Form
  >
</template>

Horizontal

<script setup lang="ts">
import { toTypedSchema } from "@vee-validate/zod";
import { z } from "zod";
import { useForm } from "vee-validate";
import { Form } from "@/components/ui/form";
import { FormField } from "@/components/ui/form";
import { FormItem } from "@/components/ui/form";
import { FormLabel } from "@/components/ui/form";
import { FormControl } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { FormMessage } from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import * as UI from "@/components/ui/form";

function useDemoForm() {
  const formSchema = toTypedSchema(
    z.object({
      login: z
        .string()
        .min(2, { message: "Login must be at least 2 characters" }),
      password: z
        .string()
        .min(2, { message: "Password must be at least 2 characters" }),
    }),
  );
  useForm({ validationSchema: formSchema });
  const onSubmit = (values: Record<string, unknown>) => console.log(values);
  return { onSubmit };
}

const Component = UI.Form;

const args = {

} satisfies Partial<
  InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
const { onSubmit } = useDemoForm();
</script>

<template>
  <Form @submit="onSubmit" class="flex w-full flex-row items-start gap-8"
    ><FormField v-slot="{ componentField }" name="login"
      ><FormItem orientation="horizontal" class="w-full"
        ><FormLabel>Login</FormLabel
        ><FormControl
          ><Input class="w-full" v-bind="componentField" /></FormControl
        ><FormMessage /></FormItem></FormField
    ><FormField v-slot="{ componentField }" name="password"
      ><FormItem orientation="horizontal" class="w-full"
        ><FormLabel>Password</FormLabel
        ><FormControl
          ><Input
            class="w-full"
            type="password"
            v-bind="componentField" /></FormControl
        ><FormMessage /></FormItem></FormField
    ><Button type="submit" class="w-fit" variant="main">Submit</Button></Form
  >
</template>

API reference

Frontfriend-owned props. Native HTML and inherited primitive props remain documented by Reka UI.

FormDescription

Prop Type Default Description
class HTMLAttributes["class"] —

FormItem

Prop Type Default Description
class HTMLAttributes["class"] —
orientation "vertical" | "horizontal" —

FormLabel

Prop Type Default Description
class HTMLAttributes["class"] —
variant "main" | "secondary" —

FormMessage

Prop Type Default Description
class HTMLAttributes["class"] —

Configuration determines the actual classes and available styling hooks. See Theming & component configuration before introducing a repeated local override.