This component was inspired by Formik and the jokes demo from Remix. This is just an experiment it's not meant for production use (yet).
interface FormActionData {
fieldErrors?: Record<string, string>;
fields?: Record<string, string>;
}
interface Field {
name: string;
label: string;
type?: string;
as?: "input" | "textarea";
}
const Field: FunctionComponent<Field> = ({
name,
label,
as: As = "input",
type,
}) => {
const actionData = useActionData<FormActionData | undefined>();
return (
<div>
<label>
{label}:{" "}
<As
defaultValue={actionData?.fields?.[name]}
aria-invalid={Boolean(actionData?.fieldErrors?.[name]) || undefined}
aria-describedby={
actionData?.fieldErrors?.[name] ? `${name}-error` : undefined
}
type={type}
name={name}
/>
</label>
{actionData?.fieldErrors?.[name] ? (
<p className="form-validation-error" role="alert" id={`${name}-error`}>
{actionData.fieldErrors?.[name] ?? ""}
</p>
) : null}
</div>
);
};and use it like
export default function NewJokeRoute() {
return (
<div>
<p>Add your own hilarious joke</p>
<Form method="post">
<Field type="text" name="name" label="Name" />
<Field as="textarea" name="content" label="Content" />
<div>
<button type="submit" className="button">
Add
</button>
</div>
</Form>
</div>
);
}