# Form Actions and use:enhance — Svelte / SvelteKit

Source: https://www.skillbyai.com/en/svelte/d-actions

> Progressively enhanced writes.

## Forms that work without JavaScript

A `+page.server.ts` can export `actions`: a `default` action or **named** actions targeted with `action="?/create"`. An action receives the `request` (read `await request.formData()`), `cookies`, `locals` and `params`. Return data to show a result, or `fail(400, { ... })` to return a validation failure with a status code; the page reads the latest result through the `form` prop. A plain `<form method="POST">` works with JavaScript disabled. Adding `use:enhance` from `$app/forms` **progressively enhances** it: the form submits with fetch, updates `form`, reruns load functions and avoids a full page reload. You can pass a callback to `enhance` to show pending states or customise the update.

## The server side of SvelteKit

Form actions handle writes, +server.ts files expose HTTP endpoints, and hooks run on every request.

![Three ideas: form actions, API routes and hooks, environment variables.](assets/figures/svelte/section-7-map.svg) — Figure 7.1 — Request, hooks, actions or endpoints, response.

## A validated signup action

Server action plus enhanced form.

```svelte
<!-- src/routes/signup/+page.server.ts
import { fail, redirect } from '@sveltejs/kit';
import type { Actions } from './$types';
import { createUser } from '$lib/server/users';

export const actions = {
  default: async ({ request }) => {
    const data = await request.formData();
    const email = String(data.get('email') ?? '');
    if (!email.includes('@')) {
      return fail(400, { email, error: 'Enter a valid email' });
    }
    await createUser(email);
    redirect(303, '/welcome');
  }
} satisfies Actions;
-->

<!-- src/routes/signup/+page.svelte -->
<script lang="ts">
  import { enhance } from '$app/forms';
  import type { PageProps } from './$types';
  let { form }: PageProps = $props();
  let pending = $state(false);
</script>

<form method="POST" use:enhance={() => {
  pending = true;
  return async ({ update }) => { await update(); pending = false; };
}}>
  <input name="email" type="email" value={form?.email ?? ''} />
  {#if form?.error}<p role="alert">{form.error}</p>{/if}
  <button disabled={pending}>{pending ? 'Signing up...' : 'Sign up'}</button>
</form>
```

## Validate on the server always

Browser validation attributes improve the experience, but the action must validate again because requests can be sent without your form. A schema library such as Zod helps.

**Quiz:** What does use:enhance add to a SvelteKit form?

- [ ] It is required or the form cannot submit
- [ ] Server-side validation
- [ ] Automatic database writes
- [x] Submission via fetch without a full page reload, while still working without JavaScript

*Answer:* Submission via fetch without a full page reload, while still working without JavaScript. Progressive enhancement layers on top of plain HTML forms.
