A better use:enhance
experience with automatic state management.
Requires Svelte v5 and runes mode.
standby
, submitting
, and submitted
statesonBeforeRequest
and onAfterResponse
callbackspnpm add svelte-form-enhanced -D
npm i svelte-form-enhanced -D
<script lang="ts">
import { enhance } from '$app/forms';
import { createFormHelper } from 'svelte-form-enhanced';
const f = createFormHelper();
</script>
<!-- Maintains submitting state for 1 second (customizable) -->
<form method="post" use:enhance={f.submitFunction}>
<!-- Use the submitting state to show loading UI: -->
<button disabled={f.state === 'submitting'} class="disabled:btn-spinner">
{f.state === 'submitting' ? 'Submitting' : 'Submit'}
</button>
</form>
type FormState = 'standby' | 'submitting' | 'submitted';
<script lang="ts">
import { createFormHelper } from 'svelte-form-enhanced';
import type { SubmitFunction } from './$types.js';
const f = createFormHelper<SubmitFunction>({
// The `ActionResult` object is now typed.
onAfterSubmit: ({ result }) => {},
});
</script>
const f = createFormHelper({ /* options */ });
type Options = Partial<{
minSubmitDuration: number; // defaults to 1000(ms)
onBeforeSubmit: (param) => void;
onAfterSubmit: (param) => void;
updateOptions: { reset?: boolean; invalidateAll?: boolean };
}>;
The function provided to the use:enhance
has been separated into two:
<script>
const f = createFormHelper({
onBeforeSubmit: () => {
console.log('Before submit');
},
onAfterSubmit: async ({ update }) => {
await update(); // default logic
console.log('After submit');
},
});
</script>
<form use:enhance={f.submitFunction}></form>
<form
use:enhance={() => {
console.log('Before submit');
return async ({ update }) => {
await update(); // default logic
console.log('After submit');
};
}}
></form>
use:enhance
will update theform
property,page.form
andpage.status
on a successful or invalid response, but only if the action is on the same page you’re submitting from. — SvelteKit Docs
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import { createFormHelper } from 'svelte-form-enhanced';
import type { SubmitFunction } from './a/$types.js';
let submittedAt = $state<Date>();
const f = createFormHelper<SubmitFunction>({
onAfterSubmit: async ({ result }) => {
if (result.type !== 'success') return; // handle error
if (!result.data) return; // type guard
submittedAt = result.data.submittedAt;
},
});
</script>
<form use:enhance={f.submitFunction} method="post" action="/a">
{#if submittedAt}
<p>
Last submitted at
<time datetime={submittedAt.toISOString()}>
{submittedAt.toLocaleTimeString()}
</time>
</p>
{/if}
<button>Submit</button>
</form>
// src/routes/a/+page.server.ts
export const actions = {
default: () => ({ submittedAt: new Date() }),
};
When a form contains multiple submit buttons:
<script lang="ts">
const f = createFormHelper({
onBeforeSubmit: ({ submitter }) => {
submitter?.classList.add('disabled:btn-spinner');
},
onAfterSubmit: async ({ submitter, update }) => {
await update();
submitter?.classList.remove('disabled:btn-spinner');
},
});
</script>
<form method="post" use:enhance={f.submitFunction}>
<!-- Loading spinner only appears on the button that is clicked -->
<button disabled={f.state === 'submitting'} formaction="?/a">A</button>
<button disabled={f.state === 'submitting'} formaction="?/b">B</button>
</form>