useFormMetadata
The
useFormMetadata
function is part of Conform's future export. These APIs are experimental and may change in minor versions. Learn more
A React hook that provides access to form-level metadata and state from any component within a FormProvider
context.
import { useFormMetadata } from '@conform-to/react/future';
const form = useFormMetadata(options);
#Parameters
options.formId?: string
Optional form identifier to target a specific form when multiple forms are rendered. If not provided, uses the nearest form context.
#Returns
A FormMetadata
object containing:
id: string
The form's unique identifier.
touched: boolean
Whether any field in the form has been touched (through intent.validate()
or the shouldValidate
option).
invalid: boolean
Whether the form currently has any validation errors.
errors: ErrorShape[] | undefined
Form-level validation errors, if any exist.
fieldErrors: Record<string, ErrorShape[]>
Object containing field-specific validation errors for all validated fields.
props: FormProps
Form props object for spreading onto the <form>
element:
{
id: string;
onSubmit: React.FormEventHandler<HTMLFormElement>;
onBlur: React.FocusEventHandler<HTMLFormElement>;
onInput: React.FormEventHandler<HTMLFormElement>;
noValidate: boolean;
}
context: FormContext<FormShape, ErrorShape>
Form context object for use with FormProvider
. Required when using useField
or useFormMetadata
in child components.
getField<FieldShape>(name): Field<FieldShape>
Method to get metadata for a specific field by name.
getFieldset<FieldShape>(name): Fieldset<FieldShape>
Method to get a fieldset object for nested object fields.
getFieldList<FieldShape>(name): Field[]
Method to get an array of field objects for array fields.
#Example
Global error summary
1import { useFormMetadata, useForm } from '@conform-to/react/future';
2
3function GlobalErrorSummary() {
4 const form = useFormMetadata();
5
6 if (Object.keys(form.fieldErrors).length === 0) {
7 return null;
8 }
9
10 return (
11 <div className="error-summary">
12 <h3>Please fix the following errors:</h3>
13 <ul>
14 {Object.entries(form.fieldErrors).map(([fieldName, errors]) => (
15 <li key={fieldName}>
16 <strong>{fieldName}:</strong> {errors.join(', ')}
17 </li>
18 ))}
19 </ul>
20 </div>
21 );
22}
23
24function App() {
25 const { form, context } = useForm({
26 onValidate({ payload, error }) {
27 if (!payload.email) {
28 error.fieldErrors.email = ['Email is required'];
29 }
30 if (!payload.password) {
31 error.fieldErrors.password = ['Password is required'];
32 }
33 return error;
34 },
35 });
36
37 return (
38 <FormProvider context={form.context}>
39 <form {...form.props}>
40 <GlobalErrorSummary />
41 <div>
42 <label>Email</label>
43 <input name="email" type="email" />
44 </div>
45 <div>
46 <label>Password</label>
47 <input name="password" type="password" />
48 </div>
49 <button type="submit">Submit</button>
50 </form>
51 </FormProvider>
52 );
53}
Custom form component
1import { useFormMetadata, useForm } from '@conform-to/react/future';
2
3function CustomForm({ children }: { children: React.ReactNode }) {
4 const form = useFormMetadata();
5
6 return (
7 <form
8 {...form.props}
9 className={form.invalid ? 'form-invalid' : 'form-valid'}
10 >
11 {/* Global form errors */}
12 {form.errors && form.errors.length > 0 && (
13 <div className="form-errors">
14 <h3>Form Error:</h3>
15 {form.errors.map((error, i) => (
16 <div key={i} className="error-message">
17 {error}
18 </div>
19 ))}
20 </div>
21 )}
22
23 {children}
24
25 {/* Smart submit button */}
26 <button
27 type="submit"
28 disabled={form.invalid}
29 className={form.touched ? 'submit-ready' : 'submit-pending'}
30 >
31 {form.invalid && form.touched ? 'Fix errors to submit' : 'Submit'}
32 </button>
33 </form>
34 );
35}
36
37function App() {
38 const { context } = useForm({
39 onValidate({ payload, error }) {
40 if (!payload.email) error.fieldErrors.email = ['Email is required'];
41 if (!payload.name) error.fieldErrors.name = ['Name is required'];
42 return error;
43 },
44 });
45
46 return (
47 <FormProvider context={form.context}>
48 <CustomForm>
49 <div>
50 <label>Name</label>
51 <input name="name" />
52 </div>
53 <div>
54 <label>Email</label>
55 <input name="email" type="email" />
56 </div>
57 </CustomForm>
58 </FormProvider>
59 );
60}