Skip to content

Form

A form component that provides validation, error handling, and accessible form controls.

Installation

bash
npx @galaxy-stack/nebula-cli@latest add form
bash
pnpm dlx @galaxy-stack/nebula-cli@latest add form
bash
yarn dlx @galaxy-stack/nebula-cli@latest add form
bash
bunx @galaxy-stack/nebula-cli@latest add form
bash
# If you have installed galaxy-design globally
galaxy-design add form

Dependencies

This component automatically installs the following dependencies:

  • React: react-hook-form, @radix-ui/react-label
  • Vue: radix-vue, vee-validate
  • Angular: @angular/forms, @radix-ng/primitives
  • React Native: react-hook-form
  • Flutter: No additional dependencies (uses built-in Form)

No manual installation needed!

Usage

Basic Example

tsx
import { useForm } from "react-hook-form"
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"

export default function FormDemo() {
  const form = useForm({
    defaultValues: {
      email: "",
    },
  })

  function onSubmit(values: { email: string }) {
    console.log(values)
  }

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
        <FormField
          control={form.control}
          name="email"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Email</FormLabel>
              <FormControl>
                <Input type="email" placeholder="Enter your email" {...field} />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <Button type="submit">Submit</Button>
      </form>
    </Form>
  )
}
vue
<script setup lang="ts">
import { useForm } from 'vee-validate'
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'

const form = useForm({
  validationSchema: {
    email: (value: string) => {
      return value ? true : 'Email is required'
    },
  },
})

function onSubmit(values: { email: string }) {
  console.log(values)
}
</script>

<template>
  <Form @submit="onSubmit">
    <FormField v-slot="{ field, errorMessage }" name="email">
      <FormItem>
        <FormLabel>Email</FormLabel>
        <FormControl>
          <Input type="email" placeholder="Enter your email" v-bind="field" />
        </FormControl>
        <FormMessage v-if="errorMessage">{{ errorMessage }}</FormMessage>
      </FormItem>
    </FormField>
    <Button type="submit">Submit</Button>
  </Form>
</template>

With Validation

tsx
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"
import { Form, FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage } from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"

const formSchema = z.object({
  email: z.string().email("Invalid email address"),
  username: z.string().min(2, "Username must be at least 2 characters"),
})

export default function FormValidation() {
  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      email: "",
      username: "",
    },
  })

  function onSubmit(values: z.infer<typeof formSchema>) {
    console.log(values)
  }

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
        <FormField
          control={form.control}
          name="username"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Username</FormLabel>
              <FormControl>
                <Input placeholder="Enter username" {...field} />
              </FormControl>
              <FormDescription>This is your public username.</FormDescription>
              <FormMessage />
            </FormItem>
          )}
        />
        <FormField
          control={form.control}
          name="email"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Email</FormLabel>
              <FormControl>
                <Input type="email" placeholder="Enter your email" {...field} />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <Button type="submit">Submit</Button>
      </form>
    </Form>
  )
}

API Reference

Components

ComponentDescriptionProps
FormRoot form containerform, onSubmit, className / class
FormFieldForm field wrapper with validationcontrol, name, render
FormItemIndividual item wrapperclassName / class
FormLabelLabel for form controlhtmlFor, className / class
FormControlWrapper for form controlclassName / class
FormDescriptionHelp text below controlclassName / class
FormMessageError message displayclassName / class

Accessibility

  • Keyboard Navigation: Tab through form fields, Enter to submit
  • Screen Reader: Uses proper <label>, aria-describedby, aria-invalid
  • Error Handling: Clear error messages with ARIA live regions
  • WCAG Compliance: WCAG 2.1 Level AA compliant

Author

Bùi Trọng Hiếu (kevinbui)

License

MIT © 2025 Bùi Trọng Hiếu (kevinbui)

Released under the MIT License.