Yugen UI

Forms with react-hook-form + zod

Combine Yugen's Field, Input, Select, Checkbox, DatePicker and Button with react-hook-form + zod for fully validated, accessible forms.

react-hook-form, zod and @hookform/resolvers are optional peers. Yugen doesn't depend on them; install them yourself with npm i react-hook-form zod @hookform/resolvers. Input / Select take register(name) directly (the ref points to the native element). Controls that aren't native form elements (DatePicker, Checkbox) use Controller. Map errors to Field with validationState="error" + validationMessage, and Field sets aria-invalid, aria-describedby and role="alert". Put noValidate on the <form> so the browser's own error bubbles don't get in the way; the submit button uses loading={isSubmitting}.
At least 8 characters, including a number.

Import

Install from npm, copy the source into your project with @yugen-ui/cli or the shadcn CLI, or import directly when working in this repo.

TSX
// npm install yugen-ui// Tuỳ chọn — tự cài: npm i react-hook-form zod @hookform/resolversimport { Controller, useForm } from "react-hook-form";import { zodResolver } from "@hookform/resolvers/zod";import { z } from "zod";import { Button, Checkbox, DatePicker, Field, Input, Select } from "yugen-ui";

Variants

Each tile is a standalone example: hover to copy, or press Code to see the source.

Server errors with setError

Errors the client can't predict (the email already exists) are set with setError(..., { shouldFocus: true }) and show up in the Field right away.

Step 1 of 3 · Sign in

Step 1 of 3: Sign in

Step-by-step validation with Wizard

Each step's validate calls trigger([...the step's fields]), which returns Promise<boolean>. On false, Wizard blocks the step and shows the error.

Usage

Do
  • Write the error messages in the zod schema in your users' language and say how to fix the problem (“Password must be at least 8 characters.”).
  • Use Controller for DatePicker, Checkbox, Combobox… and pass field.ref so the invalid field can receive focus.
  • Validate again on the server, and show server errors with setError on the right field.
Don't
  • Don't import react-hook-form / zod into library components. They are a choice made at the app level.
  • Don't show errors before people leave a field for the first time (mode "onChange" from the start is annoying).
  • Don't disable the submit button while the form is invalid. Let people click it and see the specific errors.

Props

Prop · TypeDefaultDescription
register(name)react-hook-formDefault: —Spread onto Yugen's Input / Select / Textarea: {...register("email")}.
Controllerreact-hook-formDefault: —For DatePicker (value + onValueChange), Checkbox / Switch (checked + onCheckedChange), Combobox…
Field.validationState"none" | "error" | "warning" | "success"Default: —"error" when errors.<name> is set, which puts aria-invalid on the control.
Field.validationMessageReactNodeDefault: —errors.<name>?.message, shown with role="alert" and linked through aria-describedby.
zodResolver(schema)@hookform/resolvers/zodDefault: —Uses the zod schema for validation; z.infer<typeof schema> gives the form value type.
trigger(names, { shouldFocus })(names) => Promise<boolean>Default: —Validates part of the form. Use it as WizardStep.validate.
Button.loadingbooleanDefault: —loading={isSubmitting}: a Spinner replaces the icon, the width stays the same, and aria-busy is set.