Create Own Field
This guide demonstrates how to create your own form field component using the composition pattern, rather than relying on createField. By breaking down complex forms into smaller, reusable components, this approach enhances maintainability, testability, and overall code clarity.
Benefits
- Reusability: Create reusable form components that can be shared across your application
- Maintainability: Break down complex forms into smaller, manageable pieces
- Flexibility: Easily combine and rearrange form components as needed
- Type Safety: Leverage TypeScript to ensure type safety across your form components
How to use
Basically, you can create one OwnField for your own form field component.
import {
FormControl,
FormItem,
FormLabel,
FormMessage,
FormField,
FormDescription,
} from "hookform-field";
import { ComponentType } from "react";
import get from "lodash/get";
export interface OwnFieldProps {
name: string;
label?: string;
className?: string;
inputClassName?: string;
labelClassName?: string;
component: ComponentType<any>;
baseProps?: any;
description?: string;
descriptionClassName?: string;
messageClassName?: string;
}
const OwnField = ({
name,
label,
className,
inputClassName,
labelClassName,
component: InputComp,
baseProps,
description,
descriptionClassName,
messageClassName,
}: OwnFieldProps) => {
return (
<FormField
name={name}
render={({ field, formState: { errors } }) => {
const errorState = get(errors, name);
return (
<FormItem component="div" className={className}>
{label ? (
<FormLabel component="label" className={labelClassName}>
{label}
</FormLabel>
) : null}
<FormControl>
<InputComp
{...field}
{...baseProps}
className={inputClassName}
data-state={errorState ? "error" : "idle"}
/>
</FormControl>
{errorState?.message ? null : description ? (
<FormDescription component="p" className={descriptionClassName} />
) : null}
{errorState && errorState.message ? (
<FormMessage component="p" className={messageClassName} />
) : null}
</FormItem>
);
}}
/>
);
};Or using it directly in your form component, it looks like this:
import { FormControl, FormField, FormMessage } from "hookform-field";
import { FormProvider, useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { Input } from "@/components/ui/input";
import { PasswordInput } from "@/components/ui/password-input";
const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
const SignInForm = () => {
const methods = useForm({
defaultValues: {
email: "",
password: "",
},
resolver: zodResolver(schema),
});
return (
<FormProvider {...methods}>
<form>
<FormField
name="email"
render={({ field }) => {
return (
<FormControl>
<Input {...field} />
<FormMessage className="text-red-500" />
</FormControl>
);
}}
/>
<FormField
name="password"
render={({ field }) => {
return (
<FormControl>
<PasswordInput {...field} />
<FormMessage className="text-red-500" />
</FormControl>
);
}}
/>
</form>
</FormProvider>
);
};
export default SignInForm;We believe that this is a good way to create your own form component.
But we still recommend you to use createField for simple form components.
We designed createField to be a simple and easy to use form component.