Under The Hood
frontend svelte SvelteKit 2.x

SvelteKit Form Actions: Progressive Enhancement POST

Last updated
Prerequisites:
SvelteKit routing and load functions
Understanding of HTML forms and POST
  • sveltekit
  • form-actions
  • progressive-enhancement
  • forms
  • use-form
  • server

Read at your depth

The practical view

SvelteKit form actions handle form submissions on the server. In +page.server.ts: export const actions = { default: async ({ request }) => { const data = await request.formData(); return { success: true }; } }. In the page: <form method="POST" action="?/default"> or just <form method="POST">. Use the use:enhance action to progressively enhance: it submits via fetch, updates the page with the returned result, and handles loading/validation without a full reload. The returned action data is available as form in the page. This is the standard way to do mutations in SvelteKit.

The same idea in other frameworks

react equivalent
Next.js Server Actions / React Router actions are the framework-level analogues.

Legacy vs modern

Client-only fetch handler vs progressive form action

Hand-rolled client-side submission breaks without JS and duplicates validation; a form action serves both the no-JS POST and the enhanced fetch path from one definition.

before → after
Client-only submit handler
async function onSubmit(e) {
  e.preventDefault(); // ❌ OLD: blocks native submission entirely
  await fetch('/api/login', { method: 'POST', body: new FormData(e.target) });
}
Progressive form action
export const actions = {
  default: async ({ request }) => {
    const fd = await request.formData();
    return { ok: true }; // ✅ NEW: server handles submission
  },
};

Interview gotchas

Context

This tests the progressive-enhancement contract — interviewers want to confirm you understand that form actions are genuinely server endpoints that work without JS, not fetch-only APIs.

The mechanical answer

A form action is a server route handler for POST requests to the page URL (with the ?/action-name query when named). Without JavaScript, the browser performs a native form POST: it serializes the form fields, submits to the page URL, and the SvelteKit server runs the matching action, re-runs the load functions, and returns the full re-rendered HTML page. The browser renders that response as a normal page load — no JS required. The action's return value is available to the page via the form prop, which SvelteKit serializes into the HTML. This means validation, mutation, and error display all work server-side with zero client script. use:enhance only adds the enhanced fetch path on top; it is strictly additive.

Trap

The naive answer is 'form actions are like API routes — they need fetch'. They are server endpoints that also support native browser POST. Another trap: assuming the action result is automatically applied without reload in no-JS mode — the whole page re-renders, which is correct behavior, not a failure. Candidates who explain the dual path (native POST vs fetch) show real understanding.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.