patternjavascriptsvelteModerate
SvelteKit form actions for progressive enhancement
Viewed 0 times
SvelteKit 1.x+
form actionsuse:enhanceprogressive enhancementfailredirectsveltekit forms
Error Messages
Problem
Building forms in SvelteKit with JavaScript-only fetch calls requires reimplementing loading states, error handling, and redirect logic. Form actions provide a server-side handler that works without JS.
Solution
Define actions in +page.server.ts and enhance with use:enhance:
// +page.server.ts
import { fail, redirect } from '@sveltejs/kit';
export const actions = {
login: async ({ request, cookies }) => {
const data = await request.formData();
const email = data.get('email') as string;
const password = data.get('password') as string;
if (!email || !password) {
return fail(400, { email, missing: true });
}
const user = await authenticate(email, password);
if (!user) return fail(401, { email, incorrect: true });
cookies.set('session', user.token, { path: '/' });
throw redirect(303, '/dashboard');
}
};
<!-- +page.svelte -->
<script>
import { enhance } from '$app/forms';
export let form; // form action result
</script>
<form method="POST" action="?/login" use:enhance>
<input name="email" value={form?.email ?? ''} />
{#if form?.missing}<p>Email required</p>{/if}
{#if form?.incorrect}<p>Invalid credentials</p>{/if}
<button>Login</button>
</form>
// +page.server.ts
import { fail, redirect } from '@sveltejs/kit';
export const actions = {
login: async ({ request, cookies }) => {
const data = await request.formData();
const email = data.get('email') as string;
const password = data.get('password') as string;
if (!email || !password) {
return fail(400, { email, missing: true });
}
const user = await authenticate(email, password);
if (!user) return fail(401, { email, incorrect: true });
cookies.set('session', user.token, { path: '/' });
throw redirect(303, '/dashboard');
}
};
<!-- +page.svelte -->
<script>
import { enhance } from '$app/forms';
export let form; // form action result
</script>
<form method="POST" action="?/login" use:enhance>
<input name="email" value={form?.email ?? ''} />
{#if form?.missing}<p>Email required</p>{/if}
{#if form?.incorrect}<p>Invalid credentials</p>{/if}
<button>Login</button>
</form>
Why
Form actions use native HTML forms — they work without JavaScript via standard POST. use:enhance intercepts the submit with fetch for a JavaScript-enhanced experience (no full page reload), while preserving the same server action. This is progressive enhancement.
Gotchas
- use:enhance prevents the default browser POST but still calls the same server action
- fail() returns data as form prop — redirect() must use status 303 in form actions
- Default action (?/) is used when there's only one action; named actions use ?/name
- File uploads require body parsing — use request.formData() not request.json()
Code Snippets
Custom use:enhance callback for fine-grained control
// Custom enhance callback
<form use:enhance={({ formData, cancel }) => {
// Optionally cancel the submission
// cancel();
return async ({ result, update }) => {
// result.type: 'success' | 'failure' | 'redirect' | 'error'
await update(); // apply default behavior
};
}}>Context
When building forms in SvelteKit that work without JavaScript
Revisions (0)
No revisions yet.