Usage with HTML forms
One commonly raised issue with other headless form libraries is that they lose the capabilities of HTML forms.
Namely, default HTML forms provide the following capabilities:
- Listening for
enterkey while within the form, regardless of form completion state. - The ability to call the server without
fetchor similar JavaScript calls.- Data being passed to a server during the
POST
- Data being passed to a server during the
Luckily, with HouseForm, these capabilities are preserved!
Let's start with a basic HouseForm form:
import { Form, Field } from "houseform";
import { z } from "zod";
const App = () => (
<Form onSubmit={(values) => alert(JSON.stringify(values))}>
{({ isValid, submit }) => (
<div>
<Field
name="email"
onChangeValidate={z.string().email("Must be an email")}
>
{({ value, setValue, onBlur, errors }) => (
<div>
<input
value={value}
onBlur={onBlur}
onChange={(e) => setValue(e.target.value)}
placeholder={"Email"}
/>
{errors.map((error) => (
<p key={error}>{error}</p>
))}
</div>
)}
</Field>
<button disabled={!isValid} type="submit">
Submit
</button>
</div>
)}
</Form>
);
Because of the usage of divs present in this example, this form will not have the default HTML <form> behavior allowing the user to submit the form using the enter key.
To solve this, we need to add a <form> element and use its onSubmit to call the HouseForm submit function:
import { Form, Field } from "houseform";
import { z } from "zod";
const App = () => (
<Form onSubmit={(values) => alert(JSON.stringify(values))}>
{({ isValid, submit }) => (
<form
onSubmit={(e) => {
e.preventDefault();
submit();
}}
>
<Field
name="email"
onChangeValidate={z.string().email("Must be an email")}
>
{({ value, setValue, onBlur, errors }) => (
<div>
<input
value={value}
onBlur={onBlur}
onChange={(e) => setValue(e.target.value)}
placeholder={"Email"}
/>
{errors.map((error) => (
<p key={error}>{error}</p>
))}
</div>
)}
</Field>
<button disabled={!isValid} type="submit">
Submit
</button>
</form>
)}
</Form>
);
Now, when we hit enter anywhere within the <form>, it will run the onSubmit on the HouseForm <Form> component.
Interactive Example of HouseForm with HTML Form Element
An embedded webpage:HouseForm HTML Action StackBlitz Example
Forms with Network Requests via action Attribute
If your form uses the action property to submit the form to a remote server, you can use a custom onSubmit function, a useRef, and a <form> ref's submit() function to check validation and run a form's action logic conditionally:
import { Form, Field } from "houseform";
import { z } from "zod";
import { useRef } from "react";
export default function App() {
const formRef = useRef();
return (
<Form>
{({ isValid, submit }) => (
<form
action="https://example.com/url"
method="post"
ref={formRef}
onSubmit={(e) => {
e.preventDefault();
submit().then((isValid) => {
// Is `true` if valid, `false` if not.
if (!isValid) return;
formRef.current.submit();
});
}}
>
<Field
name="email"
onBlurValidate={z.string().email("This must be an email")}
>
{({ value, setValue, onBlur, errors }) => {
return (
<div>
<input
name="email"
value={value}
onBlur={onBlur}
onChange={(e) => setValue(e.target.value)}
placeholder={"Email"}
/>
{errors.map((error) => (
<p key={error}>{error}</p>
))}
</div>
);
}}
</Field>
<button disabled={!isValid} type="submit">
Submit
</button>
</form>
)}
</Form>
);
}
With this code, your page will not refresh and execute the action logic until the form is validated by HouseForm and marked as valid.
Make sure your HTML
<input>elements also have anameassociated with them - otherwise, their values will not be submitted to theactionserver viaFormDataproperly.
Interactive Example of HouseForm with HTML Form action Property
An embedded webpage:HouseForm HTML Action StackBlitz Example