Formfield React.js component with Tailwind CSS
January 05, 2022
— views
Component
The component will add the required accessibility attributes to the input, label and error message elements thanks to react-aria.
components/form-field.tsx
import * as React from "react";
import { useField } from "@react-aria/label";
import classnames from "classnames";
interface Props {
/** the label that represents the field */
label: string;
children: React.ReactNode;
/** add extra classes to the form field */
className?: string;
/** an error message for the field */
errorMessage?: string;
/** make a form field as optional */
optional?: boolean;
}
export function FormField({ children, label, className, errorMessage, optional }: Props) {
const { labelProps, fieldProps, errorMessageProps } = useField({ label, errorMessage });
const labelClassnames = classnames("mb-1 dark:text-white");
// first child must be an input, textarea, select, etc. (form elements)
const [child, ...rest] = Array.isArray(children) ? children : [children];
const element = React.cloneElement(child as React.ReactElement<any>, {
...fieldProps,
});
return (
<div className={classnames("flex mb-3", className)}>
<label {...labelProps} className={labelClassnames}>
{label} {optional ? <span className="text-sm italic">(Optional)</span> : null}
</label>
{element}
{rest}
{errorMessage ? (
<span {...errorMessageProps} className="mt-1 font-medium text-red-500">
{errorMessage}
</span>
) : null}
</div>
);
}
tsx
Usage
// can come from a form library such as Formik or react-hook-form
const errors = {};
const values = {};
export function MyForm() {
return (
<form>
<Formfield label="Enter name" errorMessage={errors.myName}>
<MyInput name="myName" value={values.myName} />
</Formfield>
<Formfield label="Enter age" optional errorMessage={errors.age}>
<MyInput type="number" name="age" value={values.age} />
</Formfield>
</form>
);
}
tsx