breaking: remove `submitter` option from experimental form `validate(…)` method,...
authorRich Harris <richard.a.harris@gmail.com>
Thu, 20 Nov 2025 21:32:33 +0000 (16:32 -0500)
committerGitHub <noreply@github.com>
Thu, 20 Nov 2025 21:32:33 +0000 (16:32 -0500)
* breaking: remove `submitter` option from experimental form `validate()` method, always provide default submitter

* fix

* remove log

---------

Co-authored-by: Tee Ming <chewteeming01@gmail.com>
.changeset/tangy-aliens-end.md [new file with mode: 0644]
packages/kit/src/exports/public.d.ts
packages/kit/src/runtime/client/remote-functions/form.svelte.js
packages/kit/test/apps/basics/src/routes/remote/form/validate/+page.svelte
packages/kit/test/apps/basics/src/routes/remote/form/validate/form.remote.ts
packages/kit/types/index.d.ts

diff --git a/.changeset/tangy-aliens-end.md b/.changeset/tangy-aliens-end.md
new file mode 100644 (file)
index 0000000..d2dc04d
--- /dev/null
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+breaking: remove `submitter` option from experimental form `validate()` method, always provide default submitter
index d0f3663fe1711b0bd89ba26612f2d1f3d70c6d26..ce45549aa6b3cf92ff6d9ad1fb433d286092ff69 100644 (file)
@@ -2067,8 +2067,6 @@ export type RemoteForm<Input extends RemoteFormInput | void, Output> = {
                includeUntouched?: boolean;
                /** Set this to `true` to only run the `preflight` validation. */
                preflightOnly?: boolean;
-               /** Perform validation as if the form was submitted by the given button. */
-               submitter?: HTMLButtonElement | HTMLInputElement;
        }): Promise<void>;
        /** The result of the form submission */
        get result(): Output | undefined;
index c9696fa97f0d7e43fcb1767ef277f466aa9213fe..d2951e6e03767e4f5e6d49b662cc1778293e8c08 100644 (file)
@@ -522,7 +522,7 @@ export function form(id) {
                        },
                        validate: {
                                /** @type {RemoteForm<any, any>['validate']} */
-                               value: async ({ includeUntouched = false, preflightOnly = false, submitter } = {}) => {
+                               value: async ({ includeUntouched = false, preflightOnly = false } = {}) => {
                                        if (!element) return;
 
                                        const id = ++validate_id;
@@ -530,7 +530,11 @@ export function form(id) {
                                        // wait a tick in case the user is calling validate() right after set() which takes time to propagate
                                        await tick();
 
-                                       const form_data = new FormData(element, submitter);
+                                       const default_submitter = /** @type {HTMLElement | undefined} */ (
+                                               element.querySelector('button:not([type]), [type="submit"]')
+                                       );
+
+                                       const form_data = new FormData(element, default_submitter);
 
                                        /** @type {InternalRemoteFormIssue[]} */
                                        let array = [];
index 2ccb5e5ba745ee8a17405d612d082eda702c3006..eefd62be84dcde487b24c6dbdd2151c2e412d7eb 100644 (file)
@@ -5,9 +5,9 @@
        const schema = v.object({
                foo: v.picklist(['a', 'b', 'c']),
                bar: v.picklist(['d', 'e']),
-               button: v.optional(v.literal('submitter'))
+               button: v.literal('submitter')
        });
-       let submitter;
+
        let error = $state(false);
 </script>
 
 
        <input {...my_form.fields.bar.as('text')} />
 
-       <button>submit (imperative validation)</button>
+       <button {...my_form.fields.button.as('submit', 'incorrect_value')}> submit </button>
 
-       <button bind:this={submitter} {...my_form.fields.button.as('submit', 'incorrect_value')}>
-               submit
-       </button>
        {#each my_form.fields.button.issues() as issue}
                <p>{issue.message}</p>
        {/each}
-</form>
 
-<button
-       id="trigger-validate"
-       onclick={() => my_form.validate({ includeUntouched: true, submitter })}
->
+       <button {...my_form.fields.button.as('submit', 'submitter')}>
+               submit (imperative validation)
+       </button>
+</form>
+<button id="trigger-validate" onclick={() => my_form.validate({ includeUntouched: true })}>
        trigger validation
 </button>
 
index cf8a31d3aafc18ae3a157a0919673371550a4344..b9a49e1b0718697e2a4d76bad62b3a84cc43975d 100644 (file)
@@ -6,7 +6,7 @@ export const my_form = form(
        v.object({
                foo: v.picklist(['a', 'b', 'c']),
                bar: v.picklist(['d', 'e', 'f']),
-               button: v.optional(v.literal('submitter'))
+               button: v.literal('submitter')
        }),
        async (data, issue) => {
                // Test imperative validation
index 9ade41295dd5874bb3a374f3d1ace53dcc546d8d..b3611645dc44b6e41d4742e29d9055e59ddc069b 100644 (file)
@@ -2043,8 +2043,6 @@ declare module '@sveltejs/kit' {
                        includeUntouched?: boolean;
                        /** Set this to `true` to only run the `preflight` validation. */
                        preflightOnly?: boolean;
-                       /** Perform validation as if the form was submitted by the given button. */
-                       submitter?: HTMLButtonElement | HTMLInputElement;
                }): Promise<void>;
                /** The result of the form submission */
                get result(): Output | undefined;