@conform-to/react 1.17.1 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,43 +1,52 @@
1
- import type { FieldName, FormError, FormValue, Serialize, SubmissionResult, ValidationAttributes } from '@conform-to/dom/future';
1
+ import type { CustomSerialize, FieldName, FormError, FormValue, Serialize, SubmissionResult, ValidationAttributes } from '@conform-to/dom/future';
2
2
  import type { StandardSchemaV1 } from './standard-schema';
3
3
  export type Prettify<T> = {
4
4
  [K in keyof T]: T[K];
5
5
  } & {};
6
6
  /** Reference to a form element. Can be either a React ref object or a form ID string. */
7
7
  export type FormRef = React.RefObject<HTMLFormElement | HTMLFieldSetElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement | null> | string;
8
- export type InputSnapshot = {
9
- value?: string | undefined;
10
- options?: string[] | undefined;
11
- checked?: boolean | undefined;
12
- files?: File[] | undefined;
13
- };
14
- export type Control = {
8
+ export type DefaultControlValue = string | string[] | File | File[] | FileList;
9
+ export type Control<Value = DefaultControlValue, DefaultValue = Value, Payload = unknown> = {
15
10
  /**
16
- * Current value of the base input. Undefined if the registered input
17
- * is a multi-select, file input, or checkbox group.
11
+ * Current string value derived from the control payload.
18
12
  */
19
13
  value: string | undefined;
20
14
  /**
21
- * Selected options of the base input. Defined only when the registered input
22
- * is a multi-select or checkbox group.
15
+ * Checked state derived from the control payload.
23
16
  */
24
17
  checked: boolean | undefined;
25
18
  /**
26
- * Checked state of the base input. Defined only when the registered input
27
- * is a single checkbox or radio input.
19
+ * Current string array derived from the control payload.
28
20
  */
29
21
  options: string[] | undefined;
30
22
  /**
31
- * Selected files of the base input. Defined only when the registered input
32
- * is a file input.
23
+ * Current file array derived from the control payload.
33
24
  */
34
25
  files: File[] | undefined;
35
26
  /**
36
- * Registers the base input element(s). Accepts a single input or an array for groups.
27
+ * The rendered payload used as the source for base control(s).
28
+ *
29
+ * For simple native controls, this mirrors `defaultValue` / `defaultChecked`.
30
+ * For structural controls (i.e. `<fieldset>`), this is the latest payload
31
+ * snapshot that drives which hidden inputs are rendered.
32
+ */
33
+ defaultValue: DefaultValue | null | undefined;
34
+ /**
35
+ * Current payload snapshot derived from the registered base control(s).
36
+ *
37
+ * For structural controls (i.e. `<fieldset>`), this is reconstructed from
38
+ * descendant fields under the registered fieldset name.
39
+ */
40
+ payload: Payload | null | undefined;
41
+ /**
42
+ * Registers the base control element.
43
+ *
44
+ * Accepts `<input>`, `<select>`, `<textarea>`, `<fieldset>`,
45
+ * or a collection of checkbox / radio inputs with the same name.
37
46
  */
38
- register: (element: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLCollectionOf<HTMLInputElement> | NodeListOf<HTMLInputElement> | null | undefined) => void;
47
+ register: (element: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLFieldSetElement | HTMLCollectionOf<HTMLInputElement> | NodeListOf<HTMLInputElement> | null | undefined) => void;
39
48
  /**
40
- * A ref object containing the form element associated with the registered input.
49
+ * A ref object containing the form element associated with the registered base control.
41
50
  * Use this with hooks like useFormData() and useIntent().
42
51
  */
43
52
  formRef: React.RefObject<HTMLFormElement | null>;
@@ -46,21 +55,74 @@ export type Control = {
46
55
  * both [change](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event) and
47
56
  * [input](https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event) events.
48
57
  */
49
- change: (value: string | string[] | boolean | File | File[] | FileList | null) => void;
50
- /**
51
- * Emits [blur](https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event) and
52
- * [focusout](https://developer.mozilla.org/en-US/docs/Web/API/Element/focusout_event) events.
53
- * Does not actually move focus.
54
- */
55
- focus: () => void;
58
+ change: (value: Value | null) => void;
56
59
  /**
57
60
  * Emits [focus](https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event) and
58
61
  * [focusin](https://developer.mozilla.org/en-US/docs/Web/API/Element/focusin_event) events.
59
- * This does not move the actual keyboard focus to the input. Use `element.focus()` instead
62
+ *
63
+ * This does not move the actual keyboard focus to the input.
64
+ * Use [HTMLElement.focus()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus)
60
65
  * if you want to move focus to the input.
61
66
  */
67
+ focus: () => void;
68
+ /**
69
+ * Emits [blur](https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event) and
70
+ * [focusout](https://developer.mozilla.org/en-US/docs/Web/API/Element/focusout_event) events.
71
+ *
72
+ * This does not move the actual keyboard focus away from the input.
73
+ */
62
74
  blur: () => void;
63
75
  };
76
+ export type StandardControlOptions<Value extends DefaultControlValue = DefaultControlValue> = {
77
+ /**
78
+ * The initial value of the base control.
79
+ */
80
+ defaultValue?: Value | null | undefined;
81
+ /**
82
+ * A callback function that is triggered when the base control is focused.
83
+ * Use this to delegate focus to a custom input.
84
+ */
85
+ onFocus?: () => void;
86
+ };
87
+ export type CheckedControlOptions = {
88
+ /**
89
+ * Whether the base control should be checked by default.
90
+ */
91
+ defaultChecked?: boolean | undefined;
92
+ /**
93
+ * The value of a checkbox or radio control when checked.
94
+ */
95
+ value?: string;
96
+ /**
97
+ * A callback function that is triggered when the base control is focused.
98
+ * Use this to delegate focus to a custom input.
99
+ */
100
+ onFocus?: () => void;
101
+ };
102
+ export type CustomControlOptions<Value = unknown, DefaultValue = Value> = {
103
+ /**
104
+ * Initial value used to seed the control.
105
+ * For structural controls, this is the payload used to render hidden inputs.
106
+ */
107
+ defaultValue?: DefaultValue | null | undefined;
108
+ /**
109
+ * Payload parser applied to the current payload snapshot.
110
+ *
111
+ * Use this to coerce unknown DOM-derived data into a typed shape.
112
+ * Any thrown error is surfaced to the caller.
113
+ */
114
+ parse: (payload: unknown) => Value | null;
115
+ /**
116
+ * Optional serializer to convert the parsed payload back to a form value for populating the base control(s).
117
+ */
118
+ serialize?: (value: Value) => FormValue;
119
+ /**
120
+ * A callback function that is triggered when the base control is focused.
121
+ * Use this to delegate focus to a custom input.
122
+ */
123
+ onFocus?: () => void;
124
+ };
125
+ export type ControlOptions = StandardControlOptions | CheckedControlOptions | CustomControlOptions;
64
126
  export type Selector<FormValue, Result> = (formData: FormValue, lastResult: Result | undefined) => Result;
65
127
  export type UseFormDataOptions<Value = undefined> = {
66
128
  /**
@@ -77,7 +139,7 @@ export type UseFormDataOptions<Value = undefined> = {
77
139
  export type DefaultValue<Shape> = Shape extends Record<string, any> ? {
78
140
  [Key in keyof Shape]?: DefaultValue<Shape[Key]>;
79
141
  } | null | undefined : Shape extends Array<infer Item> ? Array<DefaultValue<Item>> | null | undefined : Shape extends File | File[] ? null | undefined : Shape | string | null | undefined;
80
- export type FormState<ErrorShape extends BaseErrorShape = DefaultErrorShape> = {
142
+ export type FormState<ErrorShape = any> = {
81
143
  /** Unique identifier that changes on form reset to trigger reset side effects */
82
144
  resetKey: string;
83
145
  /** Initial form values */
@@ -95,7 +157,7 @@ export type FormState<ErrorShape extends BaseErrorShape = DefaultErrorShape> = {
95
157
  /** Mapping of array field names to their item keys for React list rendering */
96
158
  listKeys: Record<string, string[]>;
97
159
  };
98
- export type FormAction<ErrorShape extends BaseErrorShape = DefaultErrorShape, Intent extends UnknownIntent | null | undefined = UnknownIntent | null, Context = {}> = SubmissionResult<ErrorShape> & {
160
+ export type FormAction<ErrorShape = any, Intent extends UnknownIntent | null | undefined = UnknownIntent | null, Context = {}> = SubmissionResult<ErrorShape> & {
99
161
  type: 'initialize' | 'server' | 'client';
100
162
  intent: Intent;
101
163
  ctx: Context;
@@ -103,7 +165,7 @@ export type FormAction<ErrorShape extends BaseErrorShape = DefaultErrorShape, In
103
165
  /**
104
166
  * Augment this interface to customize schema type inference for your schema library.
105
167
  *
106
- * @example
168
+ * **Example:**
107
169
  * ```ts
108
170
  * import type { ZodTypeAny, input, output } from 'zod';
109
171
  * import type { ZodSchemaOptions } from '@conform-to/zod/v3/future';
@@ -177,16 +239,16 @@ export type ExtractFieldConditions<T extends Record<string, FieldShapeGuard<any>
177
239
  * Resolved configuration from configureForms factory.
178
240
  * Properties with defaults are required, others remain optional.
179
241
  */
180
- export type FormsConfig<BaseErrorShape, BaseSchema, CustomFormMetadata extends Record<string, unknown>, CustomFieldMetadata extends Record<string, unknown>> = {
242
+ export type FormsConfig<BaseErrorShape, BaseSchema, SchemaErrorShape, CustomFormMetadata extends Record<string, unknown>, CustomFieldMetadata extends Record<string, unknown>> = {
181
243
  /**
182
244
  * The name of the submit button field that indicates the submission intent.
183
245
  * @default "__intent__"
184
246
  */
185
247
  intentName: string;
186
248
  /**
187
- * A custom serialization function for converting form data.
249
+ * A custom serializer for converting form values.
188
250
  */
189
- serialize: Serialize;
251
+ serialize?: CustomSerialize | undefined;
190
252
  /**
191
253
  * Determines when validation should run for the first time on a field.
192
254
  * @default "onSubmit"
@@ -201,7 +263,7 @@ export type FormsConfig<BaseErrorShape, BaseSchema, CustomFormMetadata extends R
201
263
  * Runtime type guard to check if a value is a schema.
202
264
  * Used to determine if the first argument to useForm is a schema or options object.
203
265
  *
204
- * @example
266
+ * **Example:**
205
267
  * ```ts
206
268
  * import { configureForms } from '@conform-to/react/future';
207
269
  * import {
@@ -222,7 +284,7 @@ export type FormsConfig<BaseErrorShape, BaseSchema, CustomFormMetadata extends R
222
284
  * Validates a schema against form payload.
223
285
  */
224
286
  validateSchema: <Schema extends BaseSchema>(schema: Schema, payload: Record<string, FormValue>, options?: InferOptions<Schema>) => MaybePromise<{
225
- error: FormError<string> | null;
287
+ error: FormError<SchemaErrorShape> | null;
226
288
  value?: InferOutput<Schema>;
227
289
  }>;
228
290
  /**
@@ -246,39 +308,12 @@ export type FormsConfig<BaseErrorShape, BaseSchema, CustomFormMetadata extends R
246
308
  when: DefineConditionalField;
247
309
  }) => CustomFieldMetadata;
248
310
  };
249
- export type GlobalFormOptions = {
250
- /**
251
- * The name of the submit button field that indicates the submission intent.
252
- *
253
- * @default "__intent__"
254
- */
255
- intentName: string;
256
- /**
257
- * A custom serialization function for converting form data.
258
- */
259
- serialize: Serialize;
260
- /**
261
- * Determines when validation should run for the first time on a field.
262
- *
263
- * @default "onSubmit"
264
- */
265
- shouldValidate: 'onSubmit' | 'onBlur' | 'onInput';
266
- /**
267
- * Determines when validation should run again after the field has been validated once.
268
- *
269
- * @default Same as shouldValidate
270
- */
271
- shouldRevalidate?: 'onSubmit' | 'onBlur' | 'onInput';
272
- /**
273
- * A function that defines custom metadata properties for form fields.
274
- * Useful for integrating with UI libraries or custom form components.
275
- */
276
- defineCustomMetadata?: CustomMetadataDefinition;
277
- };
278
311
  export type NonPartial<T> = {
279
312
  [K in keyof Required<T>]: T[K];
280
313
  };
281
- export type RequireKey<T, K extends keyof T> = Prettify<T & Pick<NonPartial<T>, K>>;
314
+ export type RequireKey<T, K extends keyof T> = Prettify<Omit<T, K> & {
315
+ [P in K]-?: Exclude<T[P], undefined>;
316
+ }>;
282
317
  export type BaseSchemaType = StandardSchemaV1<any, any>;
283
318
  /**
284
319
  * Infer schema input type.
@@ -288,6 +323,7 @@ export type BaseSchemaType = StandardSchemaV1<any, any>;
288
323
  export type InferInput<Schema> = Schema extends StandardSchemaV1 ? StandardSchemaV1.InferInput<Schema> : CustomSchemaTypes<Schema> extends {
289
324
  input: infer T;
290
325
  } ? T : Record<string, any>;
326
+ export type InferFormShape<Schema> = InferInput<Schema> extends Record<string, any> ? InferInput<Schema> : Record<string, any>;
291
327
  /**
292
328
  * Infer schema output type.
293
329
  * For StandardSchemaV1 schemas (zod, valibot, etc.), uses StandardSchemaV1.InferOutput.
@@ -296,7 +332,7 @@ export type InferInput<Schema> = Schema extends StandardSchemaV1 ? StandardSchem
296
332
  export type InferOutput<Schema> = Schema extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<Schema> : CustomSchemaTypes<Schema> extends {
297
333
  output: infer T;
298
334
  } ? T : undefined;
299
- export type BaseFormOptions<FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = string extends BaseErrorShape ? string : BaseErrorShape, Value = undefined, Schema = unknown> = {
335
+ export type FormOptions<FormShape extends Record<string, any> = Record<string, any>, ErrorShape = any, Value = undefined, Schema = unknown, SchemaErrorShape = ErrorShape> = {
300
336
  /** Optional form identifier. If not provided, a unique ID is automatically generated. */
301
337
  id?: string | undefined;
302
338
  /** Optional key for form state reset. When the key changes, the form resets to its initial state. */
@@ -307,6 +343,11 @@ export type BaseFormOptions<FormShape extends Record<string, any> = Record<strin
307
343
  onSubmit?: SubmitHandler<FormShape, NoInfer<ErrorShape>, NoInfer<Value>> | undefined;
308
344
  /** Initial form values. Can be a partial object matching your form structure. */
309
345
  defaultValue?: DefaultValue<FormShape> | undefined;
346
+ /**
347
+ * Override serialization for specific fields on this form and delegate the rest
348
+ * to the configured global serializer.
349
+ */
350
+ serialize?: CustomSerialize | undefined;
310
351
  /** HTML validation attributes for fields (required, minLength, pattern, etc.). */
311
352
  constraint?: Record<string, ValidationAttributes> | undefined;
312
353
  /**
@@ -316,16 +357,16 @@ export type BaseFormOptions<FormShape extends Record<string, any> = Record<strin
316
357
  schemaOptions?: InferOptions<Schema>;
317
358
  /**
318
359
  * Determines when validation should run for the first time on a field.
319
- * Overrides the global default set by FormOptionsProvider if provided.
360
+ * Overrides the default configured through `configureForms()` if provided.
320
361
  *
321
- * @default Inherits from FormOptionsProvider, or "onSubmit" if not configured
362
+ * @default Inherits from `configureForms()`, or "onSubmit" if not configured
322
363
  */
323
364
  shouldValidate?: 'onSubmit' | 'onBlur' | 'onInput' | undefined;
324
365
  /**
325
366
  * Determines when validation should run again after the field has been validated once.
326
- * Overrides the global default set by FormOptionsProvider if provided.
367
+ * Overrides the default configured through `configureForms()` if provided.
327
368
  *
328
- * @default Inherits from FormOptionsProvider, or same as shouldValidate
369
+ * @default Inherits from `configureForms()`, or same as shouldValidate
329
370
  */
330
371
  shouldRevalidate?: 'onSubmit' | 'onBlur' | 'onInput' | undefined;
331
372
  /** Error handling callback triggered when validation errors occur. By default, it focuses the first invalid field. */
@@ -334,15 +375,28 @@ export type BaseFormOptions<FormShape extends Record<string, any> = Record<strin
334
375
  onInput?: InputHandler | undefined;
335
376
  /** Blur event handler for custom focus handling logic. */
336
377
  onBlur?: BlurHandler | undefined;
337
- /** Custom validation handler. Can be skipped if using the schema property, or combined with schema to customize validation errors. */
338
- onValidate?: ValidateHandler<ErrorShape, Value, InferOutput<Schema>> | undefined;
378
+ /** Custom validation handler. Can be skipped when a schema is passed as the first argument, or combined with schema validation to customize errors. */
379
+ onValidate?: ValidateHandler<ErrorShape, Value, InferOutput<Schema>, SchemaErrorShape> | undefined;
380
+ };
381
+ /**
382
+ * The object returned by `useForm()`, containing form-level metadata,
383
+ * typed field metadata, and the intent dispatcher for programmatic updates.
384
+ */
385
+ export type FormHandle<FormShape extends Record<string, any>, ErrorShape, CustomFormMetadata extends Record<string, unknown> = {}, CustomFieldMetadata extends Record<string, unknown> = {}> = {
386
+ /** Form-level metadata and helpers. */
387
+ form: FormMetadata<ErrorShape, CustomFormMetadata, CustomFieldMetadata>;
388
+ /** Field metadata mapped from the form shape. */
389
+ fields: Fieldset<FormShape, ErrorShape, CustomFieldMetadata>;
390
+ /** Intent dispatcher for validate, reset, insert, remove, reorder, and update actions. */
391
+ intent: IntentDispatcher<FormShape>;
339
392
  };
340
- export type FormOptions<FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = string extends BaseErrorShape ? string : BaseErrorShape, Value = undefined, Schema = unknown, RequiredKeys extends keyof BaseFormOptions<FormShape, ErrorShape, Value, Schema> = never> = RequireKey<BaseFormOptions<FormShape, ErrorShape, Value, Schema>, RequiredKeys>;
341
- export interface FormContext<ErrorShape extends BaseErrorShape = DefaultErrorShape> {
393
+ export interface FormContext<ErrorShape = any> {
342
394
  /** The form's unique identifier */
343
395
  formId: string;
344
396
  /** Internal form state with validation results and field data */
345
397
  state: FormState<ErrorShape>;
398
+ /** Serializer used to derive field defaults and sync values for this form. */
399
+ serialize: Serialize;
346
400
  /** HTML validation attributes for fields */
347
401
  constraint: Record<string, ValidationAttributes> | null;
348
402
  /** Form submission event handler */
@@ -369,7 +423,7 @@ export interface IntentDispatcher<FormShape extends Record<string, any> = Record
369
423
  *
370
424
  * @param options.defaultValue - The value to reset the form to. Pass `null` to clear all fields, or omit to reset to the initial default value from `useForm`.
371
425
  *
372
- * @example
426
+ * **Example:**
373
427
  * ```tsx
374
428
  * // Reset to initial default value
375
429
  * intent.reset()
@@ -493,8 +547,8 @@ export type FormIntent<Dispatcher extends IntentDispatcher = IntentDispatcher> =
493
547
  export type IntentHandler<Signature extends (payload: any) => void = (payload: any) => void> = {
494
548
  validate?(...args: UnknownArgs<Parameters<Signature>>): boolean;
495
549
  resolve?(value: Record<string, FormValue>, ...args: Parameters<Signature>): Record<string, FormValue> | undefined;
496
- apply?<ErrorShape extends BaseErrorShape>(result: SubmissionResult<ErrorShape>, ...args: Parameters<Signature>): SubmissionResult<ErrorShape>;
497
- update?<ErrorShape extends BaseErrorShape>(state: FormState<ErrorShape>, action: FormAction<ErrorShape, {
550
+ apply?<ErrorShape>(result: SubmissionResult<ErrorShape>, ...args: Parameters<Signature>): SubmissionResult<ErrorShape>;
551
+ update?<ErrorShape>(state: FormState<ErrorShape>, action: FormAction<ErrorShape, {
498
552
  type: string;
499
553
  payload: Signature extends (payload: infer Payload) => void ? Payload : undefined;
500
554
  }, {
@@ -506,36 +560,9 @@ type BaseCombine<T, K extends PropertyKey = T extends unknown ? keyof T : never>
506
560
  export type Combine<T> = {
507
561
  [K in keyof BaseCombine<T>]: BaseCombine<T>[K];
508
562
  };
509
- /**
510
- * Extend this interface to define the base error shape for validation.
511
- *
512
- * @example
513
- * ```ts
514
- * declare module '@conform-to/react/future' {
515
- * interface CustomTypes {
516
- * errorShape: { message: string; code: string };
517
- * }
518
- * }
519
- * ```
520
- */
521
- export interface CustomTypes {
522
- }
523
- export type BaseErrorShape = CustomTypes extends {
524
- errorShape: infer Shape;
525
- } ? Shape : unknown;
526
- export type DefaultErrorShape = CustomTypes extends {
527
- errorShape: infer Shape;
528
- } ? Shape : string;
529
563
  export type SatisfyComponentProps<ElementType extends React.ElementType, CustomProps extends React.ComponentPropsWithoutRef<ElementType>> = CustomProps;
530
- /**
531
- * Interface for extending field metadata with additional properties.
532
- * @deprecated Use `configureForms()` with the `extendFieldMetadata` option for full type inference support.
533
- */
534
- export interface CustomMetadata<FieldShape = any, ErrorShape extends BaseErrorShape = DefaultErrorShape> {
535
- }
536
- export type DefaultCustomMetadata<FieldShape, ErrorShape> = keyof CustomMetadata<FieldShape, ErrorShape> extends never ? {} : CustomMetadata<FieldShape, ErrorShape>;
537
564
  /** Field metadata object containing field state, validation attributes, and nested field access methods. */
538
- export type FieldMetadata<FieldShape, ErrorShape extends BaseErrorShape = DefaultErrorShape, CustomFieldMetadata extends Record<string, unknown> = DefaultCustomMetadata<FieldShape, ErrorShape>> = Readonly<Prettify<ValidationAttributes & {
565
+ export type FieldMetadata<FieldShape, ErrorShape = any, CustomFieldMetadata extends Record<string, unknown> = {}> = Readonly<Prettify<ValidationAttributes & {
539
566
  /** Unique key for React list rendering (for array fields). */
540
567
  key: string | undefined;
541
568
  /** The field name path exactly as provided. */
@@ -570,16 +597,23 @@ export type FieldMetadata<FieldShape, ErrorShape extends BaseErrorShape = Defaul
570
597
  * For radio buttons, compare the field's `defaultValue` with the radio button's value attribute instead.
571
598
  */
572
599
  defaultChecked: boolean;
600
+ /**
601
+ * The normalized default payload at this field path.
602
+ *
603
+ * This is useful for non-native field shapes that need to render a set of
604
+ * hidden inputs before user interaction.
605
+ */
606
+ defaultPayload: unknown;
573
607
  /** Whether this field has been touched (through intent.validate() or the shouldValidate option). */
574
608
  touched: boolean;
575
609
  /** Whether this field currently has no validation errors. */
576
610
  valid: boolean;
577
611
  /** @deprecated Use `.valid` instead. This was not an intentionl breaking change and would be removed in the next minor version soon */
578
612
  invalid: boolean;
579
- /** Array of validation error messages for this field. */
580
- errors: ErrorShape[] | undefined;
581
- /** Object containing errors for all touched subfields. */
582
- fieldErrors: Record<string, ErrorShape[]>;
613
+ /** Validation error for this field. */
614
+ errors: ErrorShape | undefined;
615
+ /** Object containing validation errors for all touched subfields. */
616
+ fieldErrors: Record<string, ErrorShape>;
583
617
  /** Boolean value for the `aria-invalid` attribute. Indicates whether the field has validation errors for screen readers. */
584
618
  ariaInvalid: boolean | undefined;
585
619
  /** String value for the `aria-describedby` attribute. Contains the errorId when invalid, undefined otherwise. Merge with descriptionId manually if needed (e.g. `${metadata.descriptionId} ${metadata.ariaDescribedBy}`). */
@@ -595,21 +629,13 @@ export type FieldMetadata<FieldShape, ErrorShape extends BaseErrorShape = Defaul
595
629
  * Field metadata without custom extensions. This is the type received in `extendFieldMetadata`.
596
630
  * Equivalent to `FieldMetadata<FieldShape, ErrorShape, {}>`.
597
631
  */
598
- export type BaseFieldMetadata<FieldShape, ErrorShape extends BaseErrorShape> = FieldMetadata<FieldShape, ErrorShape, {}>;
599
- /**
600
- * @deprecated Renamed to `BaseFieldMetadata`. This will be removed in the next minor version.
601
- */
602
- export type BaseMetadata<FieldShape, ErrorShape extends BaseErrorShape> = BaseFieldMetadata<FieldShape, ErrorShape>;
603
- /**
604
- * @deprecated Use `configureForms()` with the `extendFieldMetadata` option instead.
605
- */
606
- export type CustomMetadataDefinition = <FieldShape, ErrorShape extends BaseErrorShape>(metadata: BaseFieldMetadata<FieldShape, ErrorShape>) => keyof CustomMetadata<FieldShape, ErrorShape> extends never ? {} : CustomMetadata<any, any>;
632
+ export type BaseFieldMetadata<FieldShape, ErrorShape = any> = FieldMetadata<FieldShape, ErrorShape, {}>;
607
633
  /** Fieldset object containing all form fields as properties with their respective field metadata. */
608
- export type Fieldset<FieldShape, ErrorShape extends BaseErrorShape = DefaultErrorShape, CustomFieldMetadata extends Record<string, unknown> = DefaultCustomMetadata<FieldShape, ErrorShape>> = {
634
+ export type Fieldset<FieldShape, ErrorShape = any, CustomFieldMetadata extends Record<string, unknown> = {}> = {
609
635
  [Key in keyof Combine<FieldShape>]-?: FieldMetadata<Combine<FieldShape>[Key], ErrorShape, CustomFieldMetadata>;
610
636
  };
611
637
  /** Form-level metadata and state object containing validation status, errors, and field access methods. */
612
- export type FormMetadata<ErrorShape extends BaseErrorShape = DefaultErrorShape, CustomFormMetadata extends Record<string, unknown> = {}, CustomFieldMetadata extends Record<string, unknown> = {}> = Readonly<{
638
+ export type FormMetadata<ErrorShape = any, CustomFormMetadata extends Record<string, unknown> = {}, CustomFieldMetadata extends Record<string, unknown> = {}> = Readonly<{
613
639
  /** Unique identifier that changes on form reset */
614
640
  key: string;
615
641
  /** The form's unique identifier. */
@@ -624,10 +650,10 @@ export type FormMetadata<ErrorShape extends BaseErrorShape = DefaultErrorShape,
624
650
  valid: boolean;
625
651
  /** @deprecated Use `.valid` instead. This was not an intentional breaking change and would be removed in the next minor version soon */
626
652
  invalid: boolean;
627
- /** Form-level validation errors, if any exist. */
628
- errors: ErrorShape[] | undefined;
629
- /** Object containing errors for all touched fields. */
630
- fieldErrors: Record<string, ErrorShape[]>;
653
+ /** Form-level validation error, if any exists. */
654
+ errors: ErrorShape | undefined;
655
+ /** Object containing validation errors for all touched fields. */
656
+ fieldErrors: Record<string, ErrorShape>;
631
657
  /** The form's initial default values. */
632
658
  defaultValue: Record<string, unknown>;
633
659
  /** Form props object for spreading onto the <form> element. */
@@ -653,12 +679,12 @@ export type FormMetadata<ErrorShape extends BaseErrorShape = DefaultErrorShape,
653
679
  * Form metadata without custom extensions. This is the type received in `extendFormMetadata`.
654
680
  * Equivalent to `FormMetadata<ErrorShape, {}, CustomFieldMetadata>`.
655
681
  */
656
- export type BaseFormMetadata<ErrorShape extends BaseErrorShape = DefaultErrorShape, CustomFieldMetadata extends Record<string, unknown> = {}> = FormMetadata<ErrorShape, {}, CustomFieldMetadata>;
682
+ export type BaseFormMetadata<ErrorShape = any, CustomFieldMetadata extends Record<string, unknown> = {}> = FormMetadata<ErrorShape, {}, CustomFieldMetadata>;
657
683
  export type ValidateResult<ErrorShape, Value> = FormError<ErrorShape> | null | {
658
684
  error: FormError<ErrorShape> | null;
659
685
  value?: Value;
660
686
  };
661
- export type ValidateContext<SchemaValue> = {
687
+ export type ValidateContext<SchemaValue, SchemaErrorShape> = {
662
688
  /**
663
689
  * The submitted values mapped by field name.
664
690
  * Supports nested names like `user.email` and indexed names like `items[0].id`.
@@ -668,7 +694,7 @@ export type ValidateContext<SchemaValue> = {
668
694
  * Form error object. Initially empty, but populated with schema validation
669
695
  * errors when a schema is provided and validation fails.
670
696
  */
671
- error: FormError<string>;
697
+ error: FormError<SchemaErrorShape>;
672
698
  /**
673
699
  * The submission intent derived from the button that triggered the form submission.
674
700
  */
@@ -691,18 +717,18 @@ export type ValidateContext<SchemaValue> = {
691
717
  */
692
718
  schemaValue: SchemaValue;
693
719
  };
694
- export type ValidateHandler<ErrorShape, Value, SchemaValue = undefined> = (ctx: ValidateContext<SchemaValue>) => ValidateResult<ErrorShape, Value> | Promise<ValidateResult<ErrorShape, Value>> | [
720
+ export type ValidateHandler<ErrorShape, Value, SchemaValue = undefined, SchemaErrorShape = ErrorShape> = (ctx: ValidateContext<SchemaValue, SchemaErrorShape>) => ValidateResult<ErrorShape, Value> | Promise<ValidateResult<ErrorShape, Value>> | [
695
721
  ValidateResult<ErrorShape, Value> | undefined,
696
722
  Promise<ValidateResult<ErrorShape, Value>> | undefined
697
723
  ] | undefined;
698
724
  export interface FormInputEvent extends React.FormEvent<HTMLFormElement> {
699
725
  currentTarget: EventTarget & HTMLFormElement;
700
- target: EventTarget & (HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement);
726
+ target: EventTarget & (HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLFieldSetElement);
701
727
  }
702
728
  export interface FormFocusEvent extends React.FormEvent<HTMLFormElement> {
703
729
  currentTarget: EventTarget & HTMLFormElement;
704
730
  relatedTarget: EventTarget | null;
705
- target: EventTarget & (HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement);
731
+ target: EventTarget & (HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLFieldSetElement);
706
732
  }
707
733
  export type ErrorContext<ErrorShape> = {
708
734
  formElement: HTMLFormElement;
@@ -712,7 +738,7 @@ export type ErrorContext<ErrorShape> = {
712
738
  export type ErrorHandler<ErrorShape> = (ctx: ErrorContext<ErrorShape>) => void;
713
739
  export type InputHandler = (event: FormInputEvent) => void;
714
740
  export type BlurHandler = (event: FormFocusEvent) => void;
715
- export type SubmitContext<FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = DefaultErrorShape, Value = undefined> = {
741
+ export type SubmitContext<FormShape extends Record<string, any> = Record<string, any>, ErrorShape = any, Value = undefined> = {
716
742
  formData: FormData;
717
743
  value: Value;
718
744
  update: (options: {
@@ -721,22 +747,22 @@ export type SubmitContext<FormShape extends Record<string, any> = Record<string,
721
747
  reset?: boolean | undefined;
722
748
  }) => void;
723
749
  };
724
- export type SubmitHandler<FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = DefaultErrorShape, Value = undefined> = (event: React.FormEvent<HTMLFormElement>, ctx: SubmitContext<FormShape, ErrorShape, Value>) => void | Promise<void>;
750
+ export type SubmitHandler<FormShape extends Record<string, any> = Record<string, any>, ErrorShape = any, Value = undefined> = (event: React.FormEvent<HTMLFormElement>, ctx: SubmitContext<FormShape, ErrorShape, Value>) => void | Promise<void>;
725
751
  /**
726
752
  * Infer the base error shape from a FormsConfig.
727
753
  *
728
- * @example
754
+ * **Example:**
729
755
  * ```ts
730
756
  * const { config } = configureForms({ isError: shape<{ message: string }>() });
731
757
  * type ErrorShape = InferBaseErrorShape<typeof config>; // { message: string }
732
758
  * ```
733
759
  */
734
- export type InferBaseErrorShape<Config> = Config extends FormsConfig<infer ErrorShape, any, any, any> ? ErrorShape : string;
760
+ export type InferBaseErrorShape<Config> = Config extends FormsConfig<infer ErrorShape, any, any, any, any> ? ErrorShape : string;
735
761
  /**
736
762
  * Infer the custom form metadata extension from a FormsConfig.
737
763
  * Use this to compose with FormMetadata, FieldMetadata, or Fieldset types.
738
764
  *
739
- * @example
765
+ * **Example:**
740
766
  * ```ts
741
767
  * const { config } = configureForms({
742
768
  * extendFormMetadata: (meta) => ({ customProp: meta.id })
@@ -748,12 +774,12 @@ export type InferBaseErrorShape<Config> = Config extends FormsConfig<infer Error
748
774
  * >;
749
775
  * ```
750
776
  */
751
- export type InferCustomFormMetadata<Config> = Config extends FormsConfig<any, any, infer CustomFormMetadata, any> ? CustomFormMetadata : {};
777
+ export type InferCustomFormMetadata<Config> = Config extends FormsConfig<any, any, any, infer CustomFormMetadata, any> ? CustomFormMetadata : {};
752
778
  /**
753
779
  * Infer the custom field metadata extension from a FormsConfig.
754
780
  * Use this to compose with FieldMetadata or Fieldset types.
755
781
  *
756
- * @example
782
+ * **Example:**
757
783
  * ```ts
758
784
  * const { config } = configureForms({
759
785
  * extendFieldMetadata: (meta) => ({ inputProps: { name: meta.name } })
@@ -762,13 +788,13 @@ export type InferCustomFormMetadata<Config> = Config extends FormsConfig<any, an
762
788
  * type MyFieldset<T> = Fieldset<T, InferBaseErrorShape<typeof config>, InferCustomFieldMetadata<typeof config>>;
763
789
  * ```
764
790
  */
765
- export type InferCustomFieldMetadata<Config> = Config extends FormsConfig<any, any, any, infer CustomFieldMetadata> ? CustomFieldMetadata : {};
791
+ export type InferCustomFieldMetadata<Config> = Config extends FormsConfig<any, any, any, any, infer CustomFieldMetadata> ? CustomFieldMetadata : {};
766
792
  /**
767
793
  * Transform a type to make specific keys conditional based on FieldShape.
768
794
  * Keys in ConditionalKeys will only be present when FieldShape extends the specified type.
769
795
  * Uses ConditionalFieldMetadata wrapper that RestoreFieldShape will detect and evaluate.
770
796
  *
771
- * @example
797
+ * **Example:**
772
798
  * ```ts
773
799
  * type Result = MakeConditional<
774
800
  * { textFieldProps: {...}, dateRangePickerProps: {...} },
@@ -782,5 +808,47 @@ export type MakeConditional<T, ConditionalKeys extends Record<string, unknown>>
782
808
  [K in keyof ConditionalKeys]: K extends keyof T ? ConditionalFieldMetadata<T[K], ConditionalKeys[K]> : never;
783
809
  };
784
810
  export type MaybePromise<T> = T | Promise<T>;
811
+ type BaseFieldsetProps = RequireKey<Omit<React.ComponentPropsWithoutRef<'fieldset'>, 'children' | 'defaultValue'>, 'name'> & {
812
+ /**
813
+ * Renders a hidden `<fieldset>` base control with nested hidden `<input>` elements
814
+ * derived from `defaultValue`.
815
+ */
816
+ type: 'fieldset';
817
+ /**
818
+ * Structured default value used to render nested hidden inputs.
819
+ */
820
+ defaultValue: unknown;
821
+ };
822
+ type BaseSelectProps = RequireKey<Omit<React.ComponentPropsWithoutRef<'select'>, 'children' | 'value'>, 'name' | 'defaultValue'> & {
823
+ /**
824
+ * Renders a hidden `<select>` base control.
825
+ */
826
+ type: 'select';
827
+ };
828
+ type BaseTextareaProps = RequireKey<Omit<React.ComponentPropsWithoutRef<'textarea'>, 'children' | 'value'>, 'name' | 'defaultValue'> & {
829
+ /**
830
+ * Renders a hidden `<textarea>` base control.
831
+ */
832
+ type: 'textarea';
833
+ };
834
+ type BaseCheckedInputProps = RequireKey<Omit<React.ComponentPropsWithoutRef<'input'>, 'children' | 'type' | 'checked'>, 'name' | 'defaultChecked'> & {
835
+ /**
836
+ * Renders a hidden checkbox or radio base control.
837
+ */
838
+ type: 'checkbox' | 'radio';
839
+ };
840
+ type BaseFileInputProps = RequireKey<Omit<React.ComponentPropsWithoutRef<'input'>, 'children' | 'type' | 'value' | 'checked'>, 'name'> & {
841
+ /**
842
+ * Renders a hidden `<input type="file">` base control.
843
+ */
844
+ type: 'file';
845
+ };
846
+ type BaseInputProps = RequireKey<Omit<React.ComponentPropsWithoutRef<'input'>, 'children' | 'type' | 'value' | 'checked'>, 'name' | 'defaultValue'> & {
847
+ /**
848
+ * Renders a hidden `<input type="...">` base control.
849
+ */
850
+ type?: 'color' | 'date' | 'datetime-local' | 'email' | 'hidden' | 'month' | 'number' | 'password' | 'range' | 'search' | 'tel' | 'text' | 'time' | 'url' | 'week';
851
+ };
852
+ export type BaseControlProps = BaseFieldsetProps | BaseSelectProps | BaseTextareaProps | BaseCheckedInputProps | BaseFileInputProps | BaseInputProps;
785
853
  export {};
786
854
  //# sourceMappingURL=types.d.ts.map