ข้ามไปยังเนื้อหา

Form Actions

ใน React การ submit form มักหมายถึงการดัก onSubmit event, เรียก e.preventDefault(), และส่ง data ไปยัง API ด้วย fetch เอง SvelteKit มีแนวทางที่ต่างออกไปด้วย form actions — function ฝั่ง server ที่ผูกติดกับ +page.server.js โดยตรง ทำให้ form ทำงานได้โดยไม่ต้องใช้ JavaScript เลย แล้วค่อย enhance ด้วย use:enhance เพื่อ UX แบบ SPA

React
// React + API Route
// app/api/contact/route.ts
export async function POST(req) {
const data = await req.formData();
const name = data.get('name');
const email = data.get('email');
await saveContact({ name, email });
return Response.json({ ok: true });
}
// components/ContactForm.tsx
function ContactForm() {
const [status, setStatus] = React.useState('');
async function handleSubmit(e) {
e.preventDefault();
const form = new FormData(e.target);
const res = await fetch('/api/contact', {
method: 'POST',
body: form,
});
if (res.ok) setStatus('Sent!');
}
return (
<form onSubmit={handleSubmit}>
<input name="name" placeholder="Name" required />
<input name="email" type="email" placeholder="Email" required />
<button type="submit">Send</button>
{status && <p>{status}</p>}
</form>
);
}
Svelte
<!-- src/routes/contact/+page.server.js -->
export const actions = {
default: async ({ request }) => {
const data = await request.formData();
const name = data.get('name');
const email = data.get('email');
await saveContact({ name, email });
return { success: true };
},
};
<!-- src/routes/contact/+page.svelte -->
<script>
let { form } = $props();
</script>
<form method="POST">
<input name="name" placeholder="Name" required />
<input name="email" type="email" placeholder="Email" required />
<button type="submit">Send</button>
{#if form?.success}
<p>Sent!</p>
{/if}
</form>

ข้อสังเกตสำคัญ: ไม่มี onSubmit handler, ไม่มี fetch, ไม่มี e.preventDefault() ใน Svelte — form ชี้ไปที่ server action ด้วย method="POST" และ server return ข้อมูลกลับมาผ่าน form prop โดยอัตโนมัติ

หน้าเดียวสามารถมีหลาย action ได้ เช่น form สำหรับ login และ register อยู่บนหน้าเดียวกัน

+page.server.js
export const actions = {
login: async ({ request }) => {
const data = await request.formData();
// ตรวจสอบ credentials
return { success: true };
},
register: async ({ request }) => {
const data = await request.formData();
// สร้าง account ใหม่
return { success: true };
},
};
+page.svelte
<form method="POST" action="?/login">
<input name="email" type="email" />
<input name="password" type="password" />
<button>Login</button>
</form>
<form method="POST" action="?/register">
<input name="email" type="email" />
<input name="password" type="password" />
<button>Register</button>
</form>

Form actions ทำงานได้แม้ไม่มี JavaScript (เหมือน HTML form ธรรมดา) เพิ่ม use:enhance เพื่ออัปเกรดเป็น client-side experience โดยไม่ต้องเขียน fetch logic เอง

React
// React: ต้องจัดการทุกอย่างเอง
function LoginForm() {
const [loading, setLoading] = React.useState(false);
const [error, setError] = React.useState('');
async function handleSubmit(e) {
e.preventDefault();
setLoading(true);
setError('');
try {
const res = await fetch('/api/login', {
method: 'POST',
body: new FormData(e.target),
});
const json = await res.json();
if (!json.ok) setError(json.message);
} catch {
setError('Network error');
} finally {
setLoading(false);
}
}
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" />
<input name="password" type="password" />
<button disabled={loading}>
{loading ? 'Logging in...' : 'Login'}
</button>
{error && <p className="error">{error}</p>}
</form>
);
}
Svelte
<!-- +page.server.js -->
export const actions = {
default: async ({ request, cookies }) => {
const data = await request.formData();
const user = await authenticate(
data.get('email'),
data.get('password')
);
if (!user) {
return fail(400, { message: 'Invalid credentials' });
}
cookies.set('session', user.token, { path: '/' });
redirect(303, '/dashboard');
},
};
<!-- +page.svelte -->
<script>
import { enhance } from '$app/forms';
let { form } = $props();
let loading = $state(false);
</script>
<form method="POST" use:enhance={() => {
loading = true;
return async ({ update }) => {
await update();
loading = false;
};
}}>
<input name="email" type="email" />
<input name="password" type="password" />
<button disabled={loading}>
{loading ? 'Logging in...' : 'Login'}
</button>
{#if form?.message}
<p class="error">{form.message}</p>
{/if}
</form>

use:enhance จัดการ fetch, อัปเดต form prop, และ invalidate data ให้อัตโนมัติ คุณแค่เพิ่ม callback เพื่อจัดการ loading state เท่านั้น

ใช้ fail() เพื่อส่ง validation errors กลับมาพร้อม status code ที่เหมาะสม

+page.server.js
import { fail } from '@sveltejs/kit';
export const actions = {
default: async ({ request }) => {
const data = await request.formData();
const email = data.get('email');
if (!email || !email.includes('@')) {
return fail(422, {
email,
errors: { email: 'กรุณากรอก email ที่ถูกต้อง' },
});
}
await subscribe(email);
return { success: true };
},
};
+page.svelte
<script>
let { form } = $props();
</script>
<form method="POST">
<input
name="email"
type="email"
value={form?.email ?? ''}
/>
{#if form?.errors?.email}
<p class="error">{form.errors.email}</p>
{/if}
<button>Subscribe</button>
{#if form?.success}
<p>สมัครสำเร็จแล้ว!</p>
{/if}
</form>

SvelteKit จะ repopulate ค่าใน field อัตโนมัติผ่าน form prop เมื่อ validation ล้มเหลว ไม่ต้องเขียน logic นี้เอง

คุณกำหนด SvelteKit form actions ไว้ที่ไหน?
คุณชี้ form ไปหา named action ชื่อ "create" อย่างไร?
use:enhance ทำอะไรกับ SvelteKit form?