Skip to main content

Actions de formulaire

Un fichier +page.server.js peut exporter des actions, ce qui vous permet d’envoyer des données avec POST vers le serveur en utilisant un élément de formulaire <form>.

Lorsque vous utilisez un élément <form>, la présence de JavaScript côté client est optionnelle, mais vous pouvez facilement améliorer progressivement vos interactions de formulaire avec JavaScript pour fournir la meilleure expérience utilisateur.

Actions par défaut

Dans le cas le plus simple, une page déclare une action par défaut appelée default :

src/routes/login/+page.server
/** @satisfies {import('./$types').Actions} */
export const 
const actions: {
    default: (event: any) => Promise<void>;
}
@satisfies{import('./$types').Actions}
actions
= {
default: (event: any) => Promise<void>default: async (event: anyevent) => { // TODO identifier l'utilisateur } };
import type { 
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
} from './$types';
export const
const actions: {
    default: (event: Kit.RequestEvent<Record<string, any>, string | null>) => Promise<void>;
}
actions
= {
default: (event: Kit.RequestEvent<Record<string, any>, string | null>) => Promise<void>default: async (event: Kit.RequestEvent<Record<string, any>, string | null>event) => { // TODO identifier l'utilisateur } } satisfies
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
;

Pour invoquer cette action depuis la page /login, ajouter simplement un élément <form> — aucun JavaScript n’est nécessaire :

src/routes/login/+page
<form method="POST">
	<label>
		Email
		<input name="email" type="email">
	</label>
	<label>
		Mot de passe
		<input name="password" type="password">
	</label>
	<button>Se connecter</button>
</form>

Lorsque le bouton est cliqué, le navigateur va envoyer au serveur les données du formulaire via une requête POST, en exécutant l’action par défaut.

Les actions utilisent toujours des requêtes POST, puisque les requêtes GET ne sont pas censées avoir d’effets de bord.

Nous pouvons aussi invoquer l’action depuis d’autres pages (par exemple, s’il y a un widget de connexion dans la barre de navigation du layout racine) en ajoutant l’attribut action pointant vers la page :

src/routes/+layout
<form method="POST" action="/login">
	<!-- contenu -->
</form>

Actions nommées

Plutôt qu’une seule action default par défaut, une page peut avoir autant d’actions nommées que nécessaire :

src/routes/login/+page.server
/** @satisfies {import('./$types').Actions} */
export const 
const actions: {
    login: (event: any) => Promise<void>;
    register: (event: any) => Promise<void>;
}
@satisfies{import('./$types').Actions}
actions
= {
default: async (event) => { login: (event: any) => Promise<void>login: async (event: anyevent) => { // TODO connecter l'utilisateur }, register: (event: any) => Promise<void>register: async (event: anyevent) => { // TODO inscrire l'utilisateur } };
import type { 
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
} from './$types';
export const
const actions: {
    login: (event: Kit.RequestEvent<Record<string, any>, string | null>) => Promise<void>;
    register: (event: Kit.RequestEvent<Record<string, any>, string | null>) => Promise<...>;
}
actions
= {
default: async (event) => { login: (event: Kit.RequestEvent<Record<string, any>, string | null>) => Promise<void>login: async (event: Kit.RequestEvent<Record<string, any>, string | null>event) => { // TODO connecter l'utilisateur }, register: (event: Kit.RequestEvent<Record<string, any>, string | null>) => Promise<void>register: async (event: Kit.RequestEvent<Record<string, any>, string | null>event) => { // TODO inscrire l'utilisateur } } satisfies
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
type Actions = {
    [x: string]: Kit.Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
;

Pour invoquer une action nommée, ajoutez un paramètre de requête comportant le nom de l’action préfixé par un caractère / :

src/routes/login/+page
<form method="POST" action="?/register">
src/routes/+layout
<form method="POST" action="/login?/register">

Similaire à l’attribut action, nous pouvons utiliser l’attribut formaction sur un bouton pour envoyer avec POST les mêmes données de formulaire à une action différente de celle de l’élément <form> parent :

src/routes/login/+page
<form method="POST" action="?/login">
	<label>
		Email
		<input name="email" type="email">
	</label>
	<label>
		Mot de passe
		<input name="password" type="password">
	</label>
	<button>Se connecter</button>
	<button formaction="?/register">S'inscrire</button>
</form>

Il n’est pas possible d’avoir des actions par défaut dans le même fichier que des actions nommées, car si vous ciblez une action nommée avec POST sans redirection, le paramètre de requête est persisté dans l’URL, ce qui signifie que la prochaine requête POST par défaut va cibler l’action nommée précédente.

Anatomie d’une action

Chaque action reçoit un objet RequestEvent, vous permettant de lire les données avec request.formData(). Après le traitement de la requête (par exemple, pour connecter l’utilisateur ou l’utilisatrice en définissant un cookie), l’action peut répondre avec des données qui seront disponibles via la propriété form sur la page correspondante, et via page.form dans toute l’application, jusqu’à sa prochaine mise à jour.

src/routes/login/+page.server
import * as module "$lib/server/db"db from '$lib/server/db';

/** @type {import('./$types').PageServerLoad} */
export async function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
@type{import('./$types').PageServerLoad}
load
({ cookies: Cookies

Get or set cookies related to the current request

cookies
}) {
const const user: anyuser = await module "$lib/server/db"db.getUserFromSession(cookies: Cookies

Get or set cookies related to the current request

cookies
.Cookies.get: (name: string, opts?: CookieParseOptions) => string | undefined

Gets a cookie that was previously set with cookies.set, or from the request headers.

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.parse. See documentation here
get
('sessionid'));
return { user: anyuser }; } /** @satisfies {import('./$types').Actions} */ export const
const actions: {
    login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<{
        success: boolean;
    }>;
    register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<...>;
}
@satisfies{import('./$types').Actions}
actions
= {
login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<{
    success: boolean;
}>
login
: async ({ cookies: Cookies

Get or set cookies related to the current request

cookies
, request: Request

The original request object.

request
}) => {
const const data: FormDatadata = await request: Request

The original request object.

request
.Body.formData(): Promise<FormData>formData();
const const email: FormDataEntryValue | nullemail = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('email'); const const password: FormDataEntryValue | nullpassword = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('password'); const const user: anyuser = await module "$lib/server/db"db.getUser(const email: FormDataEntryValue | nullemail); cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.set: (name: string, value: string, opts: CookieSerializeOptions & {
    path: string;
}) => void

Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.

The httpOnly and secure options are true by default (except on http://localhost, where secure is false), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The sameSite option defaults to lax.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramvalue the cookie value
@paramopts the options, passed directly to cookie.serialize. See documentation here
set
('sessionid', await module "$lib/server/db"db.createSession(const user: anyuser), { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
return { success: booleansuccess: true }; }, register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>register: async (event: RequestEvent<Record<string, any>, string | null>event) => { // TODO inscrire l'utilisateur } };
import * as module "$lib/server/db"db from '$lib/server/db';
import type { type PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>PageServerLoad, 
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
} from './$types';
export const const load: PageServerLoadload: type PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>PageServerLoad = async ({ cookies: Cookies

Get or set cookies related to the current request

cookies
}) => {
const const user: anyuser = await module "$lib/server/db"db.getUserFromSession(cookies: Cookies

Get or set cookies related to the current request

cookies
.Cookies.get: (name: string, opts?: CookieParseOptions) => string | undefined

Gets a cookie that was previously set with cookies.set, or from the request headers.

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.parse. See documentation here
get
('sessionid'));
return { user: anyuser }; }; export const
const actions: {
    login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<{
        success: boolean;
    }>;
    register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<...>;
}
actions
= {
login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<{
    success: boolean;
}>
login
: async ({ cookies: Cookies

Get or set cookies related to the current request

cookies
, request: Request

The original request object.

request
}) => {
const const data: FormDatadata = await request: Request

The original request object.

request
.Body.formData(): Promise<FormData>formData();
const const email: FormDataEntryValue | nullemail = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('email'); const const password: FormDataEntryValue | nullpassword = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('password'); const const user: anyuser = await module "$lib/server/db"db.getUser(const email: FormDataEntryValue | nullemail); cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.set: (name: string, value: string, opts: CookieSerializeOptions & {
    path: string;
}) => void

Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.

The httpOnly and secure options are true by default (except on http://localhost, where secure is false), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The sameSite option defaults to lax.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramvalue the cookie value
@paramopts the options, passed directly to cookie.serialize. See documentation here
set
('sessionid', await module "$lib/server/db"db.createSession(const user: anyuser), { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
return { success: booleansuccess: true }; }, register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>register: async (event: RequestEvent<Record<string, any>, string | null>event) => { // TODO inscrire l'utilisateur } } satisfies
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
;
src/routes/login/+page
<script>
	/** @type {import('./$types').PageProps} */
	let { data, form } = $props();
</script>

{#if form?.success}
	<!-- ce message est éphémère ; il existe car la page a été rendu en réponse à une soumission
				de formulaire. il disparaîtra si l'utilisateur recharge la page -->
	<p>Connexion réussie ! Content de vous revoir, {data.user.name}</p>
{/if}
<script lang="ts">
	import type { PageProps } from './$types';

	let { data, form }: PageProps = $props();
</script>

{#if form?.success}
	<!-- ce message est éphémère ; il existe car la page a été rendu en réponse à une soumission
				de formulaire. il disparaîtra si l'utilisateur recharge la page -->
	<p>Connexion réussie ! Content de vous revoir, {data.user.name}</p>
{/if}
Legacy mode

PageProps a été ajouté dans la version 2.16.0 de SvelteKit. Dans les versions antérieures, vous deviez typer les propriétés data et form individuellement :

+page
/** @type {{ data: import('./$types').PageData, form: import('./$types').ActionData }} */
let { let data: anydata, let form: anyform } = 
function $props(): any
namespace $props

Declares the props that a component accepts. Example:

let { optionalProp = 42, requiredProp, bindableProp = $bindable() }: { optionalProp?: number; requiredProps: string; bindableProp: boolean } = $props();

https://svelte.dev/docs/svelte/$props

$props
();
import type { import PageDataPageData, import ActionDataActionData } from './$types';

let { let data: PageDatadata, let form: ActionDataform }: { data: PageDatadata: import PageDataPageData, form: ActionDataform: import ActionDataActionData } = 
function $props(): any
namespace $props

Declares the props that a component accepts. Example:

let { optionalProp = 42, requiredProp, bindableProp = $bindable() }: { optionalProp?: number; requiredProps: string; bindableProp: boolean } = $props();

https://svelte.dev/docs/svelte/$props

$props
();

Avec Svelte 4, vous utiliseriez plutôt export let data et export let form pour déclarer les propriétés.

Erreurs de validation

Si la requête n’a pas pu être traitée à cause de données non valides, vous pouvez renvoyer des erreurs de validation — en plus des valeurs précédemment soumises par le formulaire — à l’utilisateur ou l’utilisatrice afin qu’il ou elle ré-essaye. La fonction fail vous permet de renvoyer un statut HTTP (généralement 400 ou 422, dans le cas d’erreurs de validation) avec les données. Le statut est rendu disponible via page.status et les données via form :

src/routes/login/+page.server
import { function fail(status: number): ActionFailure<undefined> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
fail
} from '@sveltejs/kit';
import * as module "$lib/server/db"db from '$lib/server/db'; /** @satisfies {import('./$types').Actions} */ export const
const actions: {
    login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
        email: string | null;
        missing: boolean;
    }> | ActionFailure<{
        ...;
    }> | {
        ...;
    }>;
    register: (event: RequestEvent<...>) => Promise<...>;
}
@satisfies{import('./$types').Actions}
actions
= {
login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
    email: string | null;
    missing: boolean;
}> | ActionFailure<{
    email: FormDataEntryValue;
    incorrect: boolean;
}> | {
    ...;
}>
login
: async ({ cookies: Cookies

Get or set cookies related to the current request

cookies
, request: Request

The original request object.

request
}) => {
const const data: FormDatadata = await request: Request

The original request object.

request
.Body.formData(): Promise<FormData>formData();
const const email: FormDataEntryValue | nullemail = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('email'); const const password: FormDataEntryValue | nullpassword = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('password'); if (!const email: FormDataEntryValue | nullemail) { return
fail<{
    email: string | null;
    missing: boolean;
}>(status: number, data: {
    email: string | null;
    missing: boolean;
}): ActionFailure<{
    email: string | null;
    missing: boolean;
}> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: string | nullemail, missing: booleanmissing: true });
} const const user: anyuser = await module "$lib/server/db"db.getUser(const email: FormDataEntryValueemail); if (!const user: anyuser || const user: anyuser.password !== module "$lib/server/db"db.hash(const password: FormDataEntryValue | nullpassword)) { return
fail<{
    email: FormDataEntryValue;
    incorrect: boolean;
}>(status: number, data: {
    email: FormDataEntryValue;
    incorrect: boolean;
}): ActionFailure<{
    email: FormDataEntryValue;
    incorrect: boolean;
}> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: FormDataEntryValueemail, incorrect: booleanincorrect: true });
} cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.set: (name: string, value: string, opts: CookieSerializeOptions & {
    path: string;
}) => void

Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.

The httpOnly and secure options are true by default (except on http://localhost, where secure is false), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The sameSite option defaults to lax.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramvalue the cookie value
@paramopts the options, passed directly to cookie.serialize. See documentation here
set
('sessionid', await module "$lib/server/db"db.createSession(const user: anyuser), { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
return { success: booleansuccess: true }; }, register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>register: async (event: RequestEvent<Record<string, any>, string | null>event) => { // TODO inscrire l'utilisateur } };
import { function fail(status: number): ActionFailure<undefined> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
fail
} from '@sveltejs/kit';
import * as module "$lib/server/db"db from '$lib/server/db'; import type {
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
} from './$types';
export const
const actions: {
    login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
        email: string | null;
        missing: boolean;
    }> | ActionFailure<{
        ...;
    }> | {
        ...;
    }>;
    register: (event: RequestEvent<...>) => Promise<...>;
}
actions
= {
login: ({ cookies, request }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
    email: string | null;
    missing: boolean;
}> | ActionFailure<{
    email: FormDataEntryValue;
    incorrect: boolean;
}> | {
    ...;
}>
login
: async ({ cookies: Cookies

Get or set cookies related to the current request

cookies
, request: Request

The original request object.

request
}) => {
const const data: FormDatadata = await request: Request

The original request object.

request
.Body.formData(): Promise<FormData>formData();
const const email: FormDataEntryValue | nullemail = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('email'); const const password: FormDataEntryValue | nullpassword = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('password'); if (!const email: FormDataEntryValue | nullemail) { return
fail<{
    email: string | null;
    missing: boolean;
}>(status: number, data: {
    email: string | null;
    missing: boolean;
}): ActionFailure<{
    email: string | null;
    missing: boolean;
}> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: string | nullemail, missing: booleanmissing: true });
} const const user: anyuser = await module "$lib/server/db"db.getUser(const email: FormDataEntryValueemail); if (!const user: anyuser || const user: anyuser.password !== module "$lib/server/db"db.hash(const password: FormDataEntryValue | nullpassword)) { return
fail<{
    email: FormDataEntryValue;
    incorrect: boolean;
}>(status: number, data: {
    email: FormDataEntryValue;
    incorrect: boolean;
}): ActionFailure<{
    email: FormDataEntryValue;
    incorrect: boolean;
}> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: FormDataEntryValueemail, incorrect: booleanincorrect: true });
} cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.set: (name: string, value: string, opts: CookieSerializeOptions & {
    path: string;
}) => void

Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.

The httpOnly and secure options are true by default (except on http://localhost, where secure is false), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The sameSite option defaults to lax.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramvalue the cookie value
@paramopts the options, passed directly to cookie.serialize. See documentation here
set
('sessionid', await module "$lib/server/db"db.createSession(const user: anyuser), { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
return { success: booleansuccess: true }; }, register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>register: async (event: RequestEvent<Record<string, any>, string | null>event) => { // TODO inscrire l'utilisateur } } satisfies
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
;

Notez que par précaution, nous renvoyons uniquement l’email à la page — pas le mot de passe.

src/routes/login/+page
<form method="POST" action="?/login">
	{#if form?.missing}<p class="error">Le champ email est requis</p>{/if}
	{#if form?.incorrect}<p class="error">Identifiants invalides !</p>{/if}
	<label>
		Email
		<input name="email" type="email" value={form?.email ?? ''}>
	</label>
	<label>
		Mot de passe
		<input name="password" type="password">
	</label>
	<button>Se connecter</button>
	<button formaction="?/register">S'inscrire</button>
</form>

Les données renvoyées doivent être sérialisables en JSON. À part ça, la structure est complètement libre. Par exemple, si vous avez plusieurs formulaires sur la même page, vous pourriez différencier à quel élément <form> les données form font référence avec une propriété id ou similaire.

Redirections

Les redirections (et les erreurs) fonctionnent exactement de la même façon que dans load :

src/routes/login/+page.server
import { function fail(status: number): ActionFailure<undefined> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
fail
, function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never

Redirect a request. When called during request handling, SvelteKit will return a redirect response. Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.

Most common status codes:

  • 303 See Other: redirect as a GET request (often used after a form POST request)
  • 307 Temporary Redirect: redirect will keep the request method
  • 308 Permanent Redirect: redirect will keep the request method, SEO will be transferred to the new page

See all redirect status codes

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
redirect
} from '@sveltejs/kit';
import * as module "$lib/server/db"db from '$lib/server/db'; /** @satisfies {import('./$types').Actions} */ export const
const actions: {
    login: ({ cookies, request, url }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
        email: FormDataEntryValue | null;
        missing: boolean;
    }> | ActionFailure<...> | {
        ...;
    }>;
    register: (event: RequestEvent<...>) => Promise<...>;
}
@satisfies{import('./$types').Actions}
actions
= {
login: ({ cookies, request, url }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
    email: FormDataEntryValue | null;
    missing: boolean;
}> | ActionFailure<...> | {
    ...;
}>
login
: async ({ cookies: Cookies

Get or set cookies related to the current request

cookies
, request: Request

The original request object.

request
, url: URL

The requested URL.

url
}) => {
const const data: FormDatadata = await request: Request

The original request object.

request
.Body.formData(): Promise<FormData>formData();
const const email: FormDataEntryValue | nullemail = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('email'); const const password: FormDataEntryValue | nullpassword = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('password'); const const user: anyuser = await module "$lib/server/db"db.getUser(const email: FormDataEntryValue | nullemail); if (!const user: anyuser) { return
fail<{
    email: FormDataEntryValue | null;
    missing: boolean;
}>(status: number, data: {
    email: FormDataEntryValue | null;
    missing: boolean;
}): ActionFailure<...> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: FormDataEntryValue | nullemail, missing: booleanmissing: true });
} if (const user: anyuser.password !== module "$lib/server/db"db.hash(const password: FormDataEntryValue | nullpassword)) { return
fail<{
    email: FormDataEntryValue | null;
    incorrect: boolean;
}>(status: number, data: {
    email: FormDataEntryValue | null;
    incorrect: boolean;
}): ActionFailure<...> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: FormDataEntryValue | nullemail, incorrect: booleanincorrect: true });
} cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.set: (name: string, value: string, opts: CookieSerializeOptions & {
    path: string;
}) => void

Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.

The httpOnly and secure options are true by default (except on http://localhost, where secure is false), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The sameSite option defaults to lax.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramvalue the cookie value
@paramopts the options, passed directly to cookie.serialize. See documentation here
set
('sessionid', await module "$lib/server/db"db.createSession(const user: anyuser), { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
if (url: URL

The requested URL.

url
.URL.searchParams: URLSearchParamssearchParams.URLSearchParams.has(name: string, value?: string): boolean

Returns a Boolean indicating if such a search parameter exists.

MDN Reference

has
('redirectTo')) {
function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never

Redirect a request. When called during request handling, SvelteKit will return a redirect response. Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.

Most common status codes:

  • 303 See Other: redirect as a GET request (often used after a form POST request)
  • 307 Temporary Redirect: redirect will keep the request method
  • 308 Permanent Redirect: redirect will keep the request method, SEO will be transferred to the new page

See all redirect status codes

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
redirect
(303, url.searchParams.get('redirectTo'));
} return { success: booleansuccess: true }; }, register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>register: async (event: RequestEvent<Record<string, any>, string | null>event) => { // TODO inscrire l'utilisateur } };
import { function fail(status: number): ActionFailure<undefined> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
fail
, function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never

Redirect a request. When called during request handling, SvelteKit will return a redirect response. Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.

Most common status codes:

  • 303 See Other: redirect as a GET request (often used after a form POST request)
  • 307 Temporary Redirect: redirect will keep the request method
  • 308 Permanent Redirect: redirect will keep the request method, SEO will be transferred to the new page

See all redirect status codes

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
redirect
} from '@sveltejs/kit';
import * as module "$lib/server/db"db from '$lib/server/db'; import type {
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
} from './$types';
export const
const actions: {
    login: ({ cookies, request, url }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
        email: FormDataEntryValue | null;
        missing: boolean;
    }> | ActionFailure<...> | {
        ...;
    }>;
    register: (event: RequestEvent<...>) => Promise<...>;
}
actions
= {
login: ({ cookies, request, url }: RequestEvent<Record<string, any>, string | null>) => Promise<ActionFailure<{
    email: FormDataEntryValue | null;
    missing: boolean;
}> | ActionFailure<...> | {
    ...;
}>
login
: async ({ cookies: Cookies

Get or set cookies related to the current request

cookies
, request: Request

The original request object.

request
, url: URL

The requested URL.

url
}) => {
const const data: FormDatadata = await request: Request

The original request object.

request
.Body.formData(): Promise<FormData>formData();
const const email: FormDataEntryValue | nullemail = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('email'); const const password: FormDataEntryValue | nullpassword = const data: FormDatadata.FormData.get(name: string): FormDataEntryValue | nullget('password'); const const user: anyuser = await module "$lib/server/db"db.getUser(const email: FormDataEntryValue | nullemail); if (!const user: anyuser) { return
fail<{
    email: FormDataEntryValue | null;
    missing: boolean;
}>(status: number, data: {
    email: FormDataEntryValue | null;
    missing: boolean;
}): ActionFailure<...> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: FormDataEntryValue | nullemail, missing: booleanmissing: true });
} if (const user: anyuser.password !== module "$lib/server/db"db.hash(const password: FormDataEntryValue | nullpassword)) { return
fail<{
    email: FormDataEntryValue | null;
    incorrect: boolean;
}>(status: number, data: {
    email: FormDataEntryValue | null;
    incorrect: boolean;
}): ActionFailure<...> (+1 overload)

Create an ActionFailure object.

@paramstatus The HTTP status code. Must be in the range 400-599.
@paramdata Data associated with the failure (e.g. validation errors)
fail
(400, { email: FormDataEntryValue | nullemail, incorrect: booleanincorrect: true });
} cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.set: (name: string, value: string, opts: CookieSerializeOptions & {
    path: string;
}) => void

Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.

The httpOnly and secure options are true by default (except on http://localhost, where secure is false), and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The sameSite option defaults to lax.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramvalue the cookie value
@paramopts the options, passed directly to cookie.serialize. See documentation here
set
('sessionid', await module "$lib/server/db"db.createSession(const user: anyuser), { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
if (url: URL

The requested URL.

url
.URL.searchParams: URLSearchParamssearchParams.URLSearchParams.has(name: string, value?: string): boolean

Returns a Boolean indicating if such a search parameter exists.

MDN Reference

has
('redirectTo')) {
function redirect(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number), location: string | URL): never

Redirect a request. When called during request handling, SvelteKit will return a redirect response. Make sure you’re not catching the thrown redirect, which would prevent SvelteKit from handling it.

Most common status codes:

  • 303 See Other: redirect as a GET request (often used after a form POST request)
  • 307 Temporary Redirect: redirect will keep the request method
  • 308 Permanent Redirect: redirect will keep the request method, SEO will be transferred to the new page

See all redirect status codes

@paramstatus The HTTP status code. Must be in the range 300-308.
@paramlocation The location to redirect to.
@throwsRedirect This error instructs SvelteKit to redirect to the specified location.
@throwsError If the provided status is invalid.
redirect
(303, url.searchParams.get('redirectTo'));
} return { success: booleansuccess: true }; }, register: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>register: async (event: RequestEvent<Record<string, any>, string | null>event) => { // TODO inscrire l'utilisateur } } satisfies
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
;

Chargement de données

Après l’exécution d’une action, la page sera re-rendue (à moins qu’une redirection ou une erreur inattendue se produise), avec la valeur de retour de l’action rendue disponible dans la page en tant que propriété form. Ceci signifie que les fonctions load de la page vont être exécutées après la fin de l’exécution d’une action.

Notez que handle est exécuté avant l’invocation de l’action, et n’est pas ré-exécutée avant les fonctions load. Ceci signifie que si, par exemple, vous utiliser handle pour remplir event.locals en fonction d’un cookie, vous devez mettre à jour event.locals lorsque vous définissez ou supprimez le cookie lors d’une action :

src/hooks.server
/** @type {import('@sveltejs/kit').Handle} */
export async function 
function handle(input: {
    event: RequestEvent;
    resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}): MaybePromise<...>
@type{import('@sveltejs/kit').Handle}
handle
({ event: RequestEvent<Partial<Record<string, string>>, string | null>event, resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve }) {
event: RequestEvent<Partial<Record<string, string>>, string | null>event.RequestEvent<Partial<Record<string, string>>, string | null>.locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
.
App.Locals.user: {
    name: string;
} | null
user
= await
function getUser(sessionid: string | undefined): {
    name: string;
}
getUser
(event: RequestEvent<Partial<Record<string, string>>, string | null>event.RequestEvent<Partial<Record<string, string>>, string | null>.cookies: Cookies

Get or set cookies related to the current request

cookies
.Cookies.get: (name: string, opts?: CookieParseOptions) => string | undefined

Gets a cookie that was previously set with cookies.set, or from the request headers.

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.parse. See documentation here
get
('sessionid'));
return resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve(event: RequestEvent<Partial<Record<string, string>>, string | null>event); }
import type { 
type Handle = (input: {
    event: RequestEvent;
    resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}) => MaybePromise<...>

The handle hook runs every time the SvelteKit server receives a request and determines the response. It receives an event object representing the request and a function called resolve, which renders the route and generates a Response. This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).

Handle
} from '@sveltejs/kit';
export const const handle: Handlehandle:
type Handle = (input: {
    event: RequestEvent;
    resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>;
}) => MaybePromise<...>

The handle hook runs every time the SvelteKit server receives a request and determines the response. It receives an event object representing the request and a function called resolve, which renders the route and generates a Response. This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).

Handle
= async ({ event: RequestEvent<Partial<Record<string, string>>, string | null>event, resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve }) => {
event: RequestEvent<Partial<Record<string, string>>, string | null>event.RequestEvent<Partial<Record<string, string>>, string | null>.locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
.
App.Locals.user: {
    name: string;
} | null
user
= await
function getUser(sessionid: string | undefined): {
    name: string;
}
getUser
(event: RequestEvent<Partial<Record<string, string>>, string | null>event.RequestEvent<Partial<Record<string, string>>, string | null>.cookies: Cookies

Get or set cookies related to the current request

cookies
.Cookies.get: (name: string, opts?: CookieParseOptions) => string | undefined

Gets a cookie that was previously set with cookies.set, or from the request headers.

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.parse. See documentation here
get
('sessionid'));
return resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>resolve(event: RequestEvent<Partial<Record<string, string>>, string | null>event); };
src/routes/account/+page.server
/** @type {import('./$types').PageServerLoad} */
export function function load(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>): MaybePromise<void | Record<string, any>>
@type{import('./$types').PageServerLoad}
load
(event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>event) {
return {
user: {
    name: string;
} | null
user
: event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>event.RequestEvent<Record<string, any>, string | null>.locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
.
App.Locals.user: {
    name: string;
} | null
user
}; } /** @satisfies {import('./$types').Actions} */ export const
const actions: {
    logout: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>;
}
@satisfies{import('./$types').Actions}
actions
= {
logout: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>logout: async (event: RequestEvent<Record<string, any>, string | null>event) => { event: RequestEvent<Record<string, any>, string | null>event.RequestEvent<Record<string, any>, string | null>.cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.delete: (name: string, opts: CookieSerializeOptions & {
    path: string;
}) => void

Deletes a cookie by setting its value to an empty string and setting the expiry date in the past.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.serialize. The path must match the path of the cookie you want to delete. See documentation here
delete
('sessionid', { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
event: RequestEvent<Record<string, any>, string | null>event.RequestEvent<Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null>.locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
.
App.Locals.user: {
    name: string;
} | null
user
= null;
} };
import type { type PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>PageServerLoad, 
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
} from './$types';
export const const load: PageServerLoadload: type PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>PageServerLoad = (event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>event) => { return {
user: {
    name: string;
} | null
user
: event: ServerLoadEvent<Record<string, any>, Record<string, any>, string | null>event.RequestEvent<Record<string, any>, string | null>.locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
.
App.Locals.user: {
    name: string;
} | null
user
}; }; export const
const actions: {
    logout: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>;
}
actions
= {
logout: (event: RequestEvent<Record<string, any>, string | null>) => Promise<void>logout: async (event: RequestEvent<Record<string, any>, string | null>event) => { event: RequestEvent<Record<string, any>, string | null>event.RequestEvent<Record<string, any>, string | null>.cookies: Cookies

Get or set cookies related to the current request

cookies
.
Cookies.delete: (name: string, opts: CookieSerializeOptions & {
    path: string;
}) => void

Deletes a cookie by setting its value to an empty string and setting the expiry date in the past.

You must specify a path for the cookie. In most cases you should explicitly set path: '/' to make the cookie available throughout your app. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children

@paramname the name of the cookie
@paramopts the options, passed directly to cookie.serialize. The path must match the path of the cookie you want to delete. See documentation here
delete
('sessionid', { path: string

Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4 Path Set-Cookie attribute } . By default, the path is considered the “default path”.

path
: '/' });
event: RequestEvent<Record<string, any>, string | null>event.RequestEvent<Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null>.locals: App.Locals

Contains custom data that was added to the request within the server handle hook.

locals
.
App.Locals.user: {
    name: string;
} | null
user
= null;
} } satisfies
type Actions = {
    [x: string]: Action<Record<string, any>, void | Record<string, any>, string | null>;
}
Actions
;

Amélioration progressive

Dans les sections précédentes, nous avons construit une action /login qui fonctionne sans JavaScript côté client — aucun fetch n’a été impliqué. C’est super, mais lorsque JavaScript est disponible, nous pouvons améliorer progressivement nos interactions de formulaire pour fournir une meilleure expérience utilisateur.

use:enhance

La manière la plus simple d’améliorer un formulaire progressivement est d’ajouter l’action use:action :

src/routes/login/+page
<script>
	import { enhance } from '$app/forms';

	/** @type {import('./$types').PageProps} */
	let { form } = $props();
</script>

<form method="POST" use:enhance>
<script lang="ts">
	import { enhance } from '$app/forms';
	import type { PageProps } from './$types';
	let { form }: PageProps = $props();
</script>

<form method="POST" use:enhance>

use:enhance peut uniquement être utilisé avec des formulaires qui ont method="POST" et pointent vers des actions définies dans un fichier +page.server.js. Cela ne fonctionnera pas avec method="GET", qui est le fonctionnement par défaut pour des formulaires sans méthode précisée. Utiliser use:enhance sur des formulaires sans method="POST" ou cibler un endpoint +server.js va provoquer une erreur.

Oui, il peut y avoir de la confusion entre l’action enhance et <form action>, qui sont toutes les deux appelées “action”. Cette documentation est pleine d’actions. Désolé.

Sans argument, use:enhance va émuler le comportement natif du navigateur, simplement sans le rechargement complet de la page. Plus précisément, cela va :

  • mettre à jour la propriété form, ainsi que page.form et page.status lors d’une réponse réussie ou invalide, mais uniquement si l’action est sur la même page que celle depuis laquelle vous soumettez. Par exemple, si vous formulaire ressemble à <form action="/somewhere/else" ..>, la propriété form et l’état page.form ne seront pas mis à jour. En effet, la soumission native d’un formulaire vous redirige vers la page sur laquelle l’action est définie. Si vous souhaitez tout de même les mettre à jour, vous devez utiliser applyAction
  • réinitialiser l’élément <form>
  • invalider toutes les données avec invalidateAll si la réponse est réussie
  • appeler goto si la réponse est une redirection
  • afficher la frontière d’erreur +error la plus proche si une erreur se produit
  • réinitialiser le focus sur l’élément approprié

Personnaliser use:enhance

Pour personnaliser le comportement, vous pouvez fournir une SubmitFunction qui sera exécutée immédiatement avant la soumission du formulaire, et (optionnellement) renvoyer un callback qui fournira une ActionResult. Notez que si vous renvoyez un callback, le comportement par défaut mentionné plus haut sera ignoré. Pour récupérer ce comportement, vous devez exécuter update.

<form
	method="POST"
	use:enhance={({ formElement, formData, action, cancel, submitter }) => {
		// `formElement` est l'élément `<form>`
		// `formData` est son objet `FormData` qui est sur le point d'être soumis
		// `action` est l'URL vers laquelle le formulaire pointe
		// exécuter `cancel()` va empêcher la soumission
		// `submitter` est l'élément `HTMLElement` qui a provoqué la soumission du formulaire

		return async ({ result, update }) => {
			// `result` est un objet `ActionResult`
			// `update` est une fonction qui déclenche la logique qui aurait été exécutée par défaut si ce
			// callback n'avait pas été défini
		};
	}}
>

Vous pouvez utiliser ces fonctions pour afficher et cacher des éléments d’interface de chargement, entre autres.

Si vous renvoyez un callback, vous pourriez avoir besoin de reproduire en partie le comportement par défaut de use:enhance, mais sans invalider toutes les données lors d’une réponse réussie. Vous pouvez faire cela grâce à applyAction :

src/routes/login/+page
<script>
	import { enhance, applyAction } from '$app/forms';

	/** @type {import('./$types').PageProps} */
	let { form } = $props();
</script>

<form
	method="POST"
	use:enhance={({ formElement, formData, action, cancel }) => {
		return async ({ result }) => {
			// `result` est un objet `ActionResult`
			if (result.type === 'redirect') {
				goto(result.location);
			} else {
				await applyAction(result);
			}
		};
	}}
>
<script lang="ts">
	import { enhance, applyAction } from '$app/forms';
	import type { PageProps } from './$types';
	let { form }: PageProps = $props();
</script>

<form
	method="POST"
	use:enhance={({ formElement, formData, action, cancel }) => {
		return async ({ result }) => {
			// `result` est un objet `ActionResult`
			if (result.type === 'redirect') {
				goto(result.location);
			} else {
				await applyAction(result);
			}
		};
	}}
>

Le comportement de applyAction(result) dépend de result.type :

  • success, failure — définit page.status en tant que result.status et met à jour form et page.form en tant que result.data (indépendamment de la source de soumission, à la différence du update venant de enhance)
  • redirect — exécute goto(result.location, { invalidateAll: true })
  • error — affiche la frontière +error la plus proche en utilisant result.error

Dans tous les cas, le focus sera réinitialisé.

Gestionnaire personnalisé d’évènement [VO]Custom event listener

Nous pouvons également implémenter de l’amélioration progressive nous-même, sans use:enhance, avec un gestionnaire d’évènement normal sur l’élément <form> :

src/routes/login/+page
<script>
	import { invalidateAll, goto } from '$app/navigation';
	import { applyAction, deserialize } from '$app/forms';

	/** @type {import('./$types').PageProps} */
	let { form } = $props();

	/** @param {SubmitEvent & { currentTarget: EventTarget & HTMLFormElement}} event */
	async function handleSubmit(event) {
		event.preventDefault();
		const data = new FormData(event.currentTarget);

		const response = await fetch(event.currentTarget.action, {
			method: 'POST',
			body: data
		});

		/** @type {import('@sveltejs/kit').ActionResult} */
		const result = deserialize(await response.text());

		if (result.type === 'success') {
			// ré-exécute toutes les fonctions `load`, en cas de succès
			await invalidateAll();
		}

		applyAction(result);
	}
</script>

<form method="POST" onsubmit={handleSubmit}>
	<!-- contenu -->
</form>
<script lang="ts">
	import { invalidateAll, goto } from '$app/navigation';
	import { applyAction, deserialize } from '$app/forms';
	import type { PageProps } from './$types';
	import type { ActionResult } from '@sveltejs/kit';
	let { form }: PageProps = $props();

	async function handleSubmit(event: SubmitEvent & { currentTarget: EventTarget & HTMLFormElement}) {
		event.preventDefault();
		const data = new FormData(event.currentTarget);

		const response = await fetch(event.currentTarget.action, {
			method: 'POST',
			body: data
		});
		const result: ActionResult = deserialize(await response.text());

		if (result.type === 'success') {
			// ré-exécute toutes les fonctions `load`, en cas de succès
			await invalidateAll();
		}

		applyAction(result);
	}
</script>

<form method="POST" onsubmit={handleSubmit}>
	<!-- contenu -->
</form>

Notez que vous avez besoin de deserialize la réponse avant de la traiter en utilisant la méthode correspondante importée depuis $app/forms. JSON.parse() ne suffit pas car les actions de formulaires — comme les fonctions load — supportent également le renvoi d’objets Date ou BigInt.

Si vous avez un fichier +server.js associé à votre +page.server.js, les requêtes fetch vont y être dirigées par défaut. Si vous souhaitez envoyer une requête POST à une action de +page.server.js, utilisez l’en-tête personnalisée x-sveltekit-action :

const const response: Responseresponse = await function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response> (+1 overload)fetch(this.action, {
	RequestInit.method?: string | undefined

A string to set request’s method.

method
: 'POST',
RequestInit.body?: BodyInit | null | undefined

A BodyInit object or null to set request’s body.

body
: data,
RequestInit.headers?: HeadersInit | undefined

A Headers object, an object literal, or an array of two-item arrays to set request’s headers.

headers
: {
'x-sveltekit-action': 'true' } });

Alternatives

Les actions de formulaire sont la méthode recommandée pour envoyer des données au serveur, puisqu’elles peuvent être améliorées progressivement, mais vous pouvez également utiliser des fichiers +server.js pour exposer (par exemple) une API JSON. Voici ce à quoi une telle interaction pourrait ressembler :

src/routes/send-message/+page
<script>
	function rerun() {
		fetch('/api/ci', {
			method: 'POST'
		});
	}
</script>

<button onclick={rerun}>Relancer la CI</button>
<script lang="ts">
	function rerun() {
		fetch('/api/ci', {
			method: 'POST'
		});
	}
</script>

<button onclick={rerun}>Relancer la CI</button>
src/routes/api/ci/+server
/** @type {import('./$types').RequestHandler} */
export function function POST(): void
@type{import('./$types').RequestHandler}
POST
() {
// faire quelque chose }
import type { 
type RequestHandler = (event: Kit.RequestEvent<Record<string, any>, string | null>) => MaybePromise<Response>
type RequestHandler = (event: Kit.RequestEvent<Record<string, any>, string | null>) => MaybePromise<Response>
RequestHandler
} from './$types';
export const const POST: RequestHandlerPOST:
type RequestHandler = (event: Kit.RequestEvent<Record<string, any>, string | null>) => MaybePromise<Response>
type RequestHandler = (event: Kit.RequestEvent<Record<string, any>, string | null>) => MaybePromise<Response>
RequestHandler
= () => {
// faire quelque chose };

GET vs POST

Comme nous venons de le voir, pour invoquer une action de formulaire, vous devez utiliser method="POST".

Certains formulaires n’ont pas besoin d’utiliser POST pour envoyer des données au serveur — les inputs de recherche par exemple. Pour ces cas-là, vous pouvez utiliser method="GET" (ou de manière équivalente, aucune method), et SvelteKit va traiter ces requêtes comme si elles venaient d’éléments <a>, utilisant le routeur client plutôt qu’une navigation impliquant le rechargement complet de la page :

<form action="/search">
	<label>
		Rechercher
		<input name="q">
	</label>
</form>

Soumettre ce formulaire va déclencher une navigation vers /search?q=... et invoquer votre fonction load, mais ne va invoquer aucune action. Comme pour les éléments <a>, vous pouvez définir les attributs data-sveltekit-reload, data-sveltekit-replacestate, data-sveltekit-keepfocus et data-sveltekit-noscroll sur l’élément <form> pour contrôler le comportement du routeur.

Sur le même sujet

Modifier cette page sur Github llms.txt