Skip to content

Releases: edmundhung/conform

v1.15.1

18 Dec 23:41
e1f9169

Choose a tag to compare

What's Changed

  • Added support for nullable constraints in getZodConstraint (#1126)
  • Fixed useControl not reflecting the input's default value in the DOM (#1121)
  • Fixed useControl not dispatching a change event when calling control.change() with the input's default value (#1122)
  • Fixed parseWithZod and parseWithValibot incorrectly treating falsy result values as errors (#1115)

New Contributors

Full Changelog: v1.15.0...v1.15.1

v1.15.0

10 Dec 00:47
a7c9e99

Choose a tag to compare

What's Changed

  • Added a getFieldValue helper to extract and validate field values from FormData or URLSearchParams. (#1112)

    import { getFieldValue } from '@conform-to/react/future';
    
    // Basic: returns `unknown`
    const email = getFieldValue(formData, 'email');
    
    // With type guard: returns `string`, throws if not a string
    const name = getFieldValue(formData, 'name', { type: 'string' });
    
    // File type: returns `File`, throws if not a File
    const avatar = getFieldValue(formData, 'avatar', { type: 'file' });
    
    // Object type: parses nested fields into `{ city: unknown, ... }`
    const address = getFieldValue<Address>(formData, 'address', { type: 'object' });
    
    // Array: returns `unknown[]`
    const tags = getFieldValue(formData, 'tags', { array: true });
    
    // Array of objects: returns `Array<{ name: unknown, ... }>`
    const items = getFieldValue<Item[]>(formData, 'items', {
      type: 'object',
      array: true,
    });
    
    // Optional: returns `string | undefined`, no error if missing
    const bio = getFieldValue(formData, 'bio', { type: 'string', optional: true });

    It also infers types from the field name:

    import { useForm, useFormData, getFieldValue } from '@conform-to/react/future';
    
    function Example() {
      const { form, fields } = useForm();
      // Retrieves the value of the `address` fieldset as an object, e.g. `{ city: unknown; ... }`
      const address = useFormData(form.id, (formData) =>
        getFieldValue(formData, fields.address.name, { type: 'object' }),
      );
    
      // ...
    }

Full Changelog: v1.14.1...v1.15.0

v1.14.1

04 Dec 11:22
fe389a8

Choose a tag to compare

What's Changed

  • Relaxed the FormConfig type to allow both lastResult and onSubmit to be optional (#1116)

Full Changelog: v1.14.0...v1.14.1

v1.14.0

01 Dec 22:07
5f30e77

Choose a tag to compare

Breaking Changes (Future APIs)

  • The intendedValue option in the report helper has been renamed to value and now works as the defaultValue when resetting the form. Previously, this option was ignored when resetting and the form would always reset to the default value. You can now use the value option to update or reset forms to a specific value. (#1079)

    // Update form to a specific value after submission
    return {
      result: report(submission, {
        value: updatedValue,
      }),
    };
    
    // Reset form to a specific value after submission
    return {
      result: report(submission, {
        reset: true,
        value: defaultValue,
      }),
    };
  • parseSubmission now strips empty values by default. This makes it easier to work with schemas directly (without coerceFormValue) since you no longer need extra validation like .min(1) for required fields. You can set stripEmptyValues: false to preserve empty values if needed. (#1110)

    const formData = new FormData();
    // Empty text input
    formData.append('name', '');
    // Empty file input
    formData.append('files[]', new File([], ''));
    parseSubmission(formData);
    // { payload: {} }
    parseSubmission(formData, { stripEmptyValues: false });
    // { payload: { name: '', files: [new File([], '')] } }

What's Changed

  • Schema-first future useForm hook with improved type inference (#1106)

    The schema option is now promoted to the first argument of useForm for better type inference:

    // Before: schema in options
    const { form, fields } = useForm({
      schema: mySchema,
      onSubmit(event, { value }) {
        // value type inference could be inconsistent
      },
    });
    
    // After: schema as first argument
    const { form, fields } = useForm(mySchema, {
      onSubmit(event, { value }) {
        // value is fully typed based on your schema
      },
    });
    • onValidate is now required when not using a schema
    • Either onSubmit or lastResult must be provided (Relaxed the type to allow both to be optional in v1.14.1)

    The old API with schema in options still works but is now deprecated. It will be removed in the next minor release.

  • Fixed parseSubmission array handling for entries ending with []. Previously, when multiple form entries had the same name ending with [] (e.g., todos[]), all items were incorrectly pushed as a single nested array element. Now they are correctly spread as individual array items. (#1108)

    const formData = new FormData();
    formData.append('todos[]', 'Buy milk');
    formData.append('todos[]', 'Walk dog');
    formData.append('todos[]', 'Write tests');
    
    parseSubmission(formData);
    // Before (incorrect): { todos: [['Buy milk', 'Walk dog', 'Write tests']] }
    
    // After (correct): { todos: ['Buy milk', 'Walk dog', 'Write tests'] }

Improvements

  • You can now import from @conform-to/zod/v3 if you need to work with v3 schema using zod v4. (Thanks @kesoji - #1090)
  • Fixed type inference for getFieldset() with interface declarations (#1097)
  • Moved lastResult logic from an effect to the render phase. Your form component may now render twice within a single lifecycle when needed, but state updates that previously spanned two separate lifecycles now complete in one. (#1103)
  • Improved FormOptions and ValidationAttributes types compatibility with exactOptionalPropertyTypes setting in tsconfig. (#1105)

Full Changelog: v1.13.3...v1.14.0

v1.13.3

07 Nov 12:35
ba4ced8

Choose a tag to compare

What's Changed

Full Changelog: v1.13.2...v1.13.3

v1.13.2

31 Oct 22:40
249901b

Choose a tag to compare

What's Changed

  • Fix change detection to avoid triggering unnecessary change events when a File input or select value hasn't actually changed (#1078)
  • Updated vitest and vitest/browser dependencies to latest versions by @chimame (#1077)

Full Changelog: v1.13.1...v1.13.2

v1.13.1

23 Oct 22:58
507e504

Choose a tag to compare

What's Changed

  • Fixed a type regression with DefaultValue that prevented setting undefined on required fields when exactOptionalPropertyTypes is enabled. (#1072)

Full Changelog: v1.13.0...v1.13.1

v1.13.0

20 Oct 10:41
6f17000

Choose a tag to compare

Breaking changes on future exports

The following metadata will no longer returns undefined to resolves behavior difference on React 18 and 19 with regards to the defaultValue property:

  • metadata.defaultValue now returns an empty string '' instead of undefined when no default value is set or the value cannot be serialized
  • metadata.defaultOptions now returns an empty array [] instead of undefined when no default options are set or the value cannot be serialized
  • metadata.defaultChecked now explicitly returns false instead of undefined when the field value is not 'on'

What's Changed

  • The intent.reset() method now accepts an optional defaultValue parameter to reset forms to a different value (#1065)

    // Clear all fields
    <button type="button" onClick={() => intent.reset({ defaultValue: null })}>
      Clear
    </button>
    
    // Restore to a specific snapshot
    <button type="button" onClick={() => intent.reset({ defaultValue: savedValue })}>
      Restore
    </button>

    Additionally, intent.update() has been optimized to behave more consistently with intent.reset(), with improved type inference when updating form value by not specifying the name option.

  • Added formRef to useControl hook (#1059)

    The useControl hook now exposes a formRef property that provides access to the form element associated with the registered input. This is particularly useful when using useControl with other form-level hooks like useFormData() and useIntent().

    const control = useControl({ defaultValue: '' });
    
    // Dispatch intent with useIntent
    const intent = useIntent(control.formRef);
    
    // The formRef automatically stays in sync even if the form attribute changes
    <input ref={control.register} form="dynamic-form-id" />;
  • Fixed an issue with coerceFormValue widening the schema type to GenericSchema | GenericSchemaAsync. It now preserves the exact schema type with compatibility to the standard schema types. (#1060)

Full Changelog: v1.12.1...v1.13.0

v1.12.1

18 Oct 10:08
38ad218

Choose a tag to compare

What's Changed

  • Fixed an issue where React DevTools would throw an error when inspecting field metadata. The Proxy now handles symbol properties used by React internals gracefully. (#1062)
  • Fixed insert and update intent type inference when field shape cannot be inferred. (#1063)

Full Changelog: v1.12.0...v1.12.1

v1.12.0

08 Oct 18:41
46ab924

Choose a tag to compare

What's changed

Metadata Customization

This update introduces a <FormOptionsProvider /> component under the future export. (#1047)

You can now define global form options, including custom metadata properties that match your form component types when integrating with UI libraries or any custom components.

import {
  FormOptionsProvider,
  type BaseMetadata,
} from '@conform-to/react/future';
import { TextField } from './components/TextField';

// Define custom metadata properties that matches the type of our custom form components
function defineCustomMetadata<FieldShape, ErrorShape>(
  metadata: BaseMetadata<FieldShape, ErrorShape>,
) {
  return {
    get textFieldProps() {
      return {
        name: metadata.name,
        defaultValue: metadata.defaultValue,
        isInvalid: !metadata.valid,
      } satisfies Partial<React.ComponentProps<typeof TextField>>;
    },
  };
}

// Extend the CustomMetadata interface with our implementation
// This makes the custom metadata types available on all field metadata objects
declare module '@conform-to/react/future' {
  interface CustomMetadata<FieldShape, ErrorShape>
    extends ReturnType<typeof defineCustomMetadata<FieldShape, ErrorShape>> {}
}

// Wrap your app with FormOptionsProvider
<FormOptionsProvider
  shouldValidate="onBlur"
  defineCustomMetadata={defineCustomMetadata}
>
  <App />
</FormOptionsProvider>;

// Use custom metadata properties in your components
function Example() {
  const { form, fields } = useForm({
    // shouldValidate now defaults to "onBlur"
  });

  return (
    <form {...form.props}>
      <TextField {...fields.email.textFieldProps} />
    </form>
  );
}

Additionally, you can now customize the base error shape globally using the CustomTypes interface:

declare module '@conform-to/react/future' {
  interface CustomTypes {
    errorShape: { message: string; code: string };
  }
}

This restricts the error shape expected from forms and improves type inference when using useField and useFormMetadata.

Improvements

  • Added ariaInvalid and ariaDescribedBy field metadata (#1047)
  • Added Zod 4 prefault schema support (#1052) - Thanks @chimame!
  • Improved docs layout setup (#1051) - Thanks @fiws!
  • Fixed an issue with checkbox not being marked as touched on form submit with async validation (#1048)
  • Fixed a bug with default values from Zod 3 default schema not being validated when coercion is enabled (#1050)

Full Changelog: v1.11.0...v1.12.0