@conform-to/dom
Version:
A set of opinionated helpers built on top of the Constraint Validation API
402 lines • 16.3 kB
TypeScript
import type { FormError, FormValue, FieldName, JsonPrimitive, CustomSerialize, Submission, SubmissionResult, UnknownObject, Serialize, StandardSchemaError, CustomError } from './types';
export declare const DEFAULT_INTENT_NAME = "__INTENT__";
/**
* Returns whether an error payload contains a meaningful value.
* Empty strings and empty arrays are treated as no error.
*/
export declare function hasError<ErrorShape>(error: ErrorShape | null | undefined): error is ErrorShape;
/**
* Normalizes a form error object by removing empty error payloads such as
* empty strings and empty arrays.
*
* Returns `null` when no form-level or field-level errors remain.
*/
export declare function normalizeFormError<ErrorShape>(error: CustomError<ErrorShape> | null): FormError<ErrorShape> | null;
/**
* Construct a form data with the submitter value.
* It utilizes the submitter argument on the FormData constructor from modern browsers
* with fallback to append the submitter value in case it is not unsupported.
*
* See https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData#parameters
*/
export declare function getFormData(form: HTMLFormElement, submitter?: HTMLElement | null): FormData;
/**
* Convert a string path into an array of segments.
*
* **Example:**
* ```js
* parsePath("object.key"); // → ['object', 'key']
* parsePath("array[0].content"); // → ['array', 0, 'content']
* parsePath("todos[]"); // → ['todos', '']
* ```
*/
export declare function parsePath(path: string | undefined): Array<string | number>;
/**
* Returns a formatted name from the path segments based on the dot and bracket notation.
*
* **Example:**
* ```js
* formatPath(['object', 'key']); // → "object.key"
* formatPath(['array', 0, 'content']); // → "array[0].content"
* formatPath(['todos', '']); // → "todos[]"
* ```
*/
export declare function formatPath(segments: Readonly<Array<string | number>>): string;
/**
* Append one more segment onto an existing path string.
*
* - segment = `undefined` ⇒ no-op
* - segment = `""` ⇒ empty brackets "[]"
* - segment = `number` ⇒ bracket notation "[n]"
* - segment = `string` ⇒ dot-notation ".prop"
*/
export declare function appendPath(path: string | undefined, segment: string | number | undefined): string;
/**
* Returns true if `prefix` is a valid leading path of `name`.
*
* **Example:**
* ```js
* isPathPrefix("foo.bar.baz", "foo.bar") // → true
* isPathPrefix("foo.bar[3].baz", "foo.bar[3]") // → true
* isPathPrefix("foo.bar[3].baz", "foo.bar") // → true
* isPathPrefix("foo.bar[3].baz", "foo.baz") // → false
* isPathPrefix("foo", "foo.bar") // → false
* ```
*/
export declare function isPathPrefix(name: string, prefix: string): boolean;
/**
* Return the segments of `fullPath` that come after the `basePath` prefix.
*
* @param fullPath Full path as a dot/bracket string or array of segments
* @param basePath Base path as a dot/bracket string or array of segments
* @returns The “tail” segments, or `null` if `fullPath` isn’t nested under `basePath`
*
* **Example:**
* ```js
* getRelativePath("foo.bar[0].qux", ["foo","bar"]) // → [0, "qux"]
* getRelativePath("a.b.c.d", ["a","b"]) // → ["c","d"]
* getRelativePath("foo", ["foo","bar"]) // → null
* ```
*/
export declare function getRelativePath(fullPath: string | Array<string | number>, basePath: string | Array<string | number>): Array<string | number> | null;
/**
* Assign a value to a target object by following the path segments.
*/
export declare function setPathValue<T extends Record<string, any>>(target: T, pathOrSegments: string | Array<string | number>, valueOrFn: unknown | ((current: unknown) => unknown), options?: {
clone?: boolean;
silent?: boolean;
}): T;
/**
* Retrive the value from a target object by following the path segments.
*/
export declare function getPathValue(target: unknown, pathOrSegments: string | Array<string | number>): unknown;
/**
* Parse `FormData` or `URLSearchParams` into a submission object.
* This function structures the form values based on the naming convention.
* It also includes all the field names and extracts the intent from the submission.
*
* See https://conform.guide/api/react/future/parseSubmission
*
* **Example:**
* ```ts
* const formData = new FormData();
*
* formData.append('email', 'test@example.com');
* formData.append('password', 'secret');
*
* parseSubmission(formData)
* // {
* // payload: { email: 'test@example.com', password: 'secret' },
* // fields: ['email', 'password'],
* // intent: null,
* // }
*
* // If you have an intent field
* formData.append('intent', 'login');
* parseSubmission(formData, { intentName: 'intent' })
* // {
* // payload: { email: 'test@example.com', password: 'secret' },
* // fields: ['email', 'password'],
* // intent: 'login',
* // }
* ```
*/
export declare function parseSubmission(formData: FormData | URLSearchParams, options?: {
/**
* The name of the submit button field that indicates the submission intent.
* Defaults to `__INTENT__`.
*/
intentName?: string | undefined;
/**
* A function to exclude specific form fields from being parsed.
* Return `true` to skip the entry.
*/
skipEntry?: ((name: string) => boolean) | undefined;
/**
* Whether to strip empty values (empty strings, empty files, arrays with all empty values)
* from the submission payload. Defaults to `false`.
*
* @deprecated This option will be removed in a future minor release.
* If you are using a schema library like Zod or Valibot, our integration
* already strips empty values for you. There is no need to use this option.
*/
stripEmptyValues?: boolean | undefined;
}): Submission;
/**
* Creates a SubmissionResult object from a submission, adding validation results and target values.
* This function will remove all files in the submission payload by default since
* file inputs cannot be initialized with files.
* You can specify `keepFiles: true` to keep the files if needed.
*
* See https://conform.guide/api/react/future/report
*
* **Example:**
* ```ts
* // Report the submission with the field errors
* report(submission, {
* error: {
* fieldErrors: {
* email: ['Invalid email format'],
* password: ['Password is required'],
* },
* })
*
* // Report the submission with a form error
* report(submission, {
* error: {
* formErrors: ['Invalid credentials'],
* },
* })
*
* // Reset the form
* report(submission, {
* reset: true,
* })
* ```
*/
export declare function report(submission: Submission, options?: {
keepFiles?: false;
error?: null;
value?: Record<string, FormValue> | null;
hideFields?: string[];
reset?: boolean;
}): SubmissionResult<never, Exclude<JsonPrimitive | FormDataEntryValue, File>>;
export declare function report(submission: Submission, options: {
keepFiles: true;
error?: null;
value?: Record<string, FormValue> | null;
hideFields?: string[];
reset?: boolean;
}): SubmissionResult<never>;
export declare function report<ErrorShape>(submission: Submission, options: {
keepFiles?: false;
error: CustomError<ErrorShape> | null;
value?: Record<string, FormValue> | null;
hideFields?: string[];
reset?: boolean;
}): SubmissionResult<ErrorShape, Exclude<JsonPrimitive | FormDataEntryValue, File>>;
export declare function report<ErrorShape>(submission: Submission, options: {
keepFiles: true;
error: CustomError<ErrorShape> | null;
value?: Record<string, FormValue> | null;
hideFields?: string[];
reset?: boolean;
}): SubmissionResult<ErrorShape>;
export declare function report(submission: Submission, options: {
keepFiles?: false;
error: StandardSchemaError;
value?: Record<string, FormValue> | null;
hideFields?: string[];
reset?: boolean;
}): SubmissionResult<string[], Exclude<JsonPrimitive | FormDataEntryValue, File>>;
export declare function report(submission: Submission, options: {
keepFiles: true;
error: StandardSchemaError;
value?: Record<string, FormValue> | null;
hideFields?: string[];
reset?: boolean;
}): SubmissionResult<string[]>;
export declare function report<ErrorShape>(submission: Submission, options?: {
keepFiles?: boolean;
error?: CustomError<ErrorShape> | null;
value?: Record<string, FormValue> | null;
hideFields?: string[];
reset?: boolean;
}): SubmissionResult<ErrorShape>;
/**
* A utility function that checks whether the current form data differs from the default values.
*
* See https://conform.guide/api/react/future/isDirty
*
* **Example: Enable a submit button only if the form is dirty**
*
* ```tsx
* const dirty = useFormData(
* formRef,
* (formData) => isDirty(formData, { defaultValue }) ?? false,
* );
*
* return (
* <button type="submit" disabled={!dirty}>
* Save changes
* </button>
* );
* ```
*/
export declare function isDirty(
/**
* The current form data to compare. It can be:
*
* - A `FormData` object
* - A `URLSearchParams` object
* - A plain object that was parsed from form data (i.e. `submission.payload`)
*/
formData: FormData | URLSearchParams | FormValue | null, options?: {
/**
* An object representing the default values of the form to compare against.
* Defaults to an empty object if not provided.
*/
defaultValue?: unknown | undefined;
/**
* The name of the submit button that triggered the submission.
* It will be excluded from the dirty comparison.
*/
intentName?: string | undefined;
/**
* A function to serialize values in defaultValue before comparing them to the form data.
* If not provided, a default serializer is used that behaves as follows:
*
* - string / File:
* - Returned as-is
* - boolean:
* - true → 'on'
* - false → null
* - number / bigint:
* - Converted to string using `.toString()`
* - Date:
* - Converted to UTC datetime string without trailing `Z` (e.g. `2026-01-01T12:00:00.000`)
*/
serialize?: CustomSerialize | undefined;
/**
* A function to exclude specific fields from the comparison.
* Useful for ignoring hidden inputs like CSRF tokens or internal fields added by frameworks
* (e.g. Next.js uses hidden inputs to support server actions).
*
* **Example:**
* ```ts
* isDirty(formData, {
* skipEntry: (name) => name === 'csrf-token',
* });
* ```
*/
skipEntry?: ((name: string) => boolean) | undefined;
}): boolean | undefined;
/**
* Convert an unknown value into something acceptable for HTML form submission.
* Returns `undefined` when the value cannot be represented in form data.
*
* Input -> Output:
* - string -> string
* - null -> '' (empty string)
* - boolean -> 'on' | '' (checked semantics)
* - number | bigint -> value.toString()
* - Date -> value.toISOString() without trailing `Z`
* - File -> File
* - FileList -> File[]
* - Array -> string[] or File[] if all items serialize to the same kind; otherwise undefined
* - anything else -> undefined
*/
export declare function defaultSerialize(value: unknown): ReturnType<Serialize>;
/**
* Recursively serializes a value using the provided serialize function,
* collapsing empty leaves (`null`, `''`, empty files) to `undefined`
* and removing empty containers (objects with no remaining keys, empty arrays).
*
* When serialize returns `undefined` for a value (i.e. it can't be represented
* as form data), the raw value is kept and recursed into if it's an object or array.
*
* Single-element arrays where the element is a string or undefined are unwrapped
* to handle the case where a multi-value field (e.g. checkboxes) has only one value.
*/
export declare function normalize(value: unknown, serialize?: Serialize, name?: string): unknown;
/**
* Retrieve a field value from FormData with optional type guards.
*
* **Example:**
*
* ```ts
* // Basic field access: return `unknown`
* const email = getFieldValue(formData, 'email');
* // String type: returns `string`
* const name = getFieldValue(formData, 'name', { type: 'string' });
* // File type: returns `File`
* const avatar = getFieldValue(formData, 'avatar', { type: 'file' });
* // Object type: returns { city: unknown, ... }
* const address = getFieldValue<Address>(formData, 'address', { type: 'object' });
* // Array: returns `unknown[]`
* const tags = getFieldValue(formData, 'tags', { array: true });
* // Array with object type: returns `Array<{ name: unknown, ... }>`
* const items = getFieldValue<Item[]>(formData, 'items', { type: 'object', array: true });
* // Optional string type: returns `string | undefined`
* const bio = getFieldValue(formData, 'bio', { type: 'string', optional: true });
* ```
*/
export declare function getFieldValue<FieldShape extends Array<Record<string, unknown>>>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
type: 'object';
array: true;
optional: true;
}): FieldShape extends Array<infer Item extends Record<string, unknown>> ? Array<UnknownObject<Item>> | undefined : never;
export declare function getFieldValue<FieldShape extends Array<Record<string, unknown>>>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
type: 'object';
array: true;
}): FieldShape extends Array<infer Item extends Record<string, unknown>> ? Array<UnknownObject<Item>> : never;
export declare function getFieldValue<FieldShape extends Record<string, unknown> = Record<string, unknown>>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
type: 'object';
optional: true;
}): UnknownObject<FieldShape> | undefined;
export declare function getFieldValue<FieldShape extends Record<string, unknown> = Record<string, unknown>>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
type: 'object';
}): UnknownObject<FieldShape>;
export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
type: 'string';
array: true;
optional: true;
}): string[] | undefined;
export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
type: 'string';
array: true;
}): string[];
export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
type: 'string';
optional: true;
}): string | undefined;
export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
type: 'string';
}): string;
export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
type: 'file';
array: true;
optional: true;
}): File[] | undefined;
export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
type: 'file';
array: true;
}): File[];
export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
type: 'file';
optional: true;
}): File | undefined;
export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
type: 'file';
}): File;
export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
array: true;
optional: true;
}): Array<unknown> | undefined;
export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options: {
array: true;
}): Array<unknown>;
export declare function getFieldValue<FieldShape>(formData: FormData | URLSearchParams, name: FieldName<FieldShape>, options?: {
optional?: boolean;
}): unknown;
//# sourceMappingURL=formdata.d.ts.map