Beignet API reference
    Preparing search index...

    Module @beignet/react-hook-form

    @beignet/react-hook-form

    React Hook Form integration for Beignet

    This package provides automatic form validation using your contract's body schema. Works with any Standard Schema library (Zod, Valibot, ArkType, etc.).

    npm install @beignet/react-hook-form @beignet/core react-hook-form @hookform/resolvers react
    

    This package requires TypeScript 5.0 or higher for proper type inference.

    import { createReactHookForm } from "@beignet/react-hook-form";
    import { createTodo } from "@/features/todos/contracts";

    const rhf = createReactHookForm();

    function CreateTodoForm() {
    const { useForm } = rhf(createTodo);
    const form = useForm({
    defaultValues: {
    title: "",
    completed: false,
    },
    });

    const onSubmit = form.handleSubmit((values) => {
    // values is typed as: { title: string; completed?: boolean }
    console.log("Creating todo:", values);
    });

    return (
    <form onSubmit={onSubmit}>
    <input
    {...form.register("title")}
    placeholder="What needs to be done?"
    />
    {form.formState.errors.title && (
    <p className="error">{form.formState.errors.title.message}</p>
    )}

    <label>
    <input type="checkbox" {...form.register("completed")} />
    Completed
    </label>

    <button type="submit" disabled={form.formState.isSubmitting}>
    Create Todo
    </button>
    </form>
    );
    }
    import { createReactHookForm } from "@beignet/react-hook-form";
    import { useMutation } from "@tanstack/react-query";
    import { rq } from "@/client/rq";
    import { createTodo } from "@/features/todos/contracts";

    const rhf = createReactHookForm();

    function CreateTodoForm() {
    const { useForm } = rhf(createTodo);
    const form = useForm({
    defaultValues: { title: "" },
    });

    const mutation = useMutation(
    rq(createTodo).mutationOptions()
    );

    const onSubmit = form.handleSubmit((values) => {
    mutation.mutate({ body: values });
    });

    return (
    <form onSubmit={onSubmit}>
    <input {...form.register("title")} placeholder="Title" />
    {form.formState.errors.title && (
    <p>{form.formState.errors.title.message}</p>
    )}

    <button type="submit" disabled={mutation.isPending}>
    {mutation.isPending ? "Creating..." : "Create"}
    </button>

    {mutation.isError && (
    <p className="error">{mutation.error.message}</p>
    )}
    </form>
    );
    }

    If you need to disable the schema resolver (e.g., for partial form handling):

    const { useForm } = rhf(createTodo);
    const form = useForm({
    resolverEnabled: false, // Disable schema validation
    defaultValues: { title: "" },
    });

    You can pass either a contract builder or its config:

    import { createReactHookForm } from "@beignet/react-hook-form";
    import { createTodo } from "@/features/todos/contracts";

    const rhf = createReactHookForm();

    // Using ContractBuilder directly
    const { useForm } = rhf(createTodo);

    // Or using the contract config
    const { useForm } = rhf(createTodo.config);

    Creates a React Hook Form adapter factory.

    const rhf = createReactHookForm();
    

    Creates a React Hook Form adapter for a contract.

    const adapter = rhf(createTodo);
    

    Returns a React Hook Form useForm result with the contract's body schema as resolver.

    const form = adapter.useForm({
    defaultValues?: { ... },
    resolverEnabled?: boolean, // default: true
    // ...other React Hook Form options
    });

    Form values are automatically typed based on the contract's body schema:

    // Contract definition
    const createTodo = todos
    .post("/api/todos")
    .body(z.object({
    title: z.string().min(1),
    description: z.string().optional(),
    completed: z.boolean().optional(),
    }))
    .responses({ 201: TodoSchema });

    // Form values are inferred
    const rhf = createReactHookForm();
    const form = rhf(createTodo).useForm();
    form.register("title"); // ✓ Valid
    form.register("description"); // ✓ Valid
    form.register("invalid"); // ✗ Type error

    This package uses the @hookform/resolvers/standard-schema resolver, which works with any Standard Schema compatible library:

    • Zod - z.object({ ... })
    • Valibot - v.object({ ... })
    • ArkType - type({ ... })

    The resolver validates:

    1. On blur - When a field loses focus
    2. On change - After first submission attempt
    3. On submit - Before calling your submit handler

    Validation errors are available via form.formState.errors:

    {form.formState.errors.title && (
    <span className="error">
    {form.formState.errors.title.message}
    </span>
    )}
    import { createReactHookForm } from "@beignet/react-hook-form";
    import { useMutation } from "@tanstack/react-query";
    import { rq } from "@/client/rq";
    import { updateProfile } from "@/features/profile/contracts";

    const rhf = createReactHookForm();

    function ProfileForm({ profile }) {
    const { useForm } = rhf(updateProfile);
    const form = useForm({
    defaultValues: {
    name: profile.name,
    email: profile.email,
    bio: profile.bio ?? "",
    },
    });

    const mutation = useMutation(
    rq(updateProfile).mutationOptions({
    onSuccess: () => {
    toast.success("Profile updated!");
    },
    })
    );

    const onSubmit = form.handleSubmit((values) => {
    mutation.mutate({ body: values });
    });

    const { errors, isDirty, isSubmitting } = form.formState;

    return (
    <form onSubmit={onSubmit}>
    <div>
    <label htmlFor="name">Name</label>
    <input id="name" {...form.register("name")} />
    {errors.name && <span className="error">{errors.name.message}</span>}
    </div>

    <div>
    <label htmlFor="email">Email</label>
    <input id="email" type="email" {...form.register("email")} />
    {errors.email && <span className="error">{errors.email.message}</span>}
    </div>

    <div>
    <label htmlFor="bio">Bio</label>
    <textarea id="bio" {...form.register("bio")} />
    {errors.bio && <span className="error">{errors.bio.message}</span>}
    </div>

    <button type="submit" disabled={!isDirty || isSubmitting}>
    {isSubmitting ? "Saving..." : "Save Changes"}
    </button>
    </form>
    );
    }

    MIT

    Type Aliases

    ReactHookFormContractAdapter

    Functions

    createReactHookForm