This Sveltekit library passes temporary data to the next request, usually in form actions and endpoints. It's useful for displaying a success or failure message after a POST, which should not always be displayed at the form, rather as a message on the page that the request was redirected to.
Since it's a temporary message it's also known as a "flash message", especially known from PHP apps, since it's easy to add this functionality with PHP's built-in session handling. With SvelteKit it's a bit harder, but this library was made to alleviate that, encouraging well-behaved web apps that Redirects after Post.
pnpm i -D sveltekit-flash-message
npm i -D sveltekit-flash-message
In src/app.d.ts
, add the type for the flash message to App.PageData
as an optional property called flash
. It can be as simple as a string
, or something more advanced. It has to be serializable though, so only JSON-friendly data structures. For example:
src/app.d.ts
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
interface PageData {
flash?: { type: 'success' | 'error'; message: string };
}
// interface PageState {}
// interface Platform {}
}
}
export {};
If you're not using any load functions, this is a simple step. Create a src/routes/+layout.server.ts
file with the following content:
src/routes/+layout.server.ts
export { load } from 'sveltekit-flash-message/server';
But most likely you already have a top-level load
function, in which case you can import loadFlash
and wrap your load function with it:
src/routes/+layout.server.ts
import { loadFlash } from 'sveltekit-flash-message/server';
export const load = loadFlash(async (event) => {
const data = { someOther: 'data' };
return data;
});
Note: There cannot be any additional loadFlash
calls in routes below, as the flash cookie is deleted when it is found the first time.
Import getFlash
in a layout or page component to display the flash message. getFlash
will return a store that you'll use to access the message:
src/routes/+layout.svelte
<script lang="ts">
import { getFlash } from 'sveltekit-flash-message';
import { page } from '$app/stores';
const flash = getFlash(page);
</script>
{#if $flash}
{@const bg = $flash.type == 'success' ? '#3D9970' : '#FF4136'}
<div style:background-color={bg} class="flash">{$flash.message}</div>
{/if}
To send a flash message from the server, import redirect
from sveltekit-flash-message/server
and use it in load functions and form actions.
Note: With SvelteKit 2, you don't need to throw the redirect, just call redirect
. If you're still on SvelteKit 1, throw
the function call.
import { redirect } from 'sveltekit-flash-message/server'
// The most specific: Redirect with a specific HTTP status to a specific location.
redirect(
status: number,
location: string,
message: App.PageData['flash'],
event: RequestEvent | Cookies
)
// Makes a 303 redirect to a specific location.
redirect(
location: string,
message: App.PageData['flash'],
event: RequestEvent | Cookies
)
// Makes a 303 redirect to the current location.
redirect(
message: App.PageData['flash'],
event: RequestEvent
)
// For compatibility, the sveltekit signature can also be used, which will send no flash message.
redirect(
status: number,
location: string,
)
src/routes/todos/+page.server.ts
import { redirect } from 'sveltekit-flash-message/server';
export const actions = {
default: async ({ request, locals, cookies }) => {
const form = await request.formData();
await api('POST', `/todos/${locals.userid}`, {
text: form.get('text')
});
redirect('/', { type: 'success', message: "That's the entrepreneur spirit!" }, cookies);
}
};
src/routes/todos/+server.ts
import type { RequestEvent } from '@sveltejs/kit';
import { redirect } from 'sveltekit-flash-message/server';
export const POST = async ({ cookies }) => {
redirect('/', { type: 'success', message: 'Endpoint POST successful!' }, cookies);
};
If you want to display a flash message without redirecting, as an error message when validation fails for example, you can use the setFlash
function:
import { fail } from '@sveltejs/kit';
import { setFlash } from 'sveltekit-flash-message/server';
export const actions = {
default: async ({ request, cookies }) => {
const form = await request.formData();
if (!form.get('text')) {
setFlash({ type: 'error', message: 'Please enter text.' }, cookies);
return fail(400);
}
}
};
If you want to update the flash message on the client, use getFlash
in any component:
src/routes/some-route/+page.svelte
<script>
import { getFlash } from 'sveltekit-flash-message';
import { page } from '$app/stores';
const flash = getFlash(page);
function showMessage() {
$flash = { type: 'success', message: 'Updated from other component!' };
}
</script>
<button on:click={showMessage}>Show flash message</button>
This will of course not set a cookie for the next request, it'll only update the flash message on the client.
The flash message will update automatically on redirect or navigation, but when using fetch, you must call updateFlash
afterwards:
<script lang="ts">
import { updateFlash } from 'sveltekit-flash-message';
import { page } from '$app/stores';
async function submitForm(e: Event) {
const form = e.target as HTMLFormElement;
const body = new FormData(e.target as HTMLFormElement);
await fetch(form.action, { method: 'POST', body });
await updateFlash(page);
}
</script>
<form method="POST" action="/test" on:submit|preventDefault={submitForm}>
<input type="text" name="test" value="TEST" />
<button>Submit with fetch</button>
</form>
updateFlash
can take a second parameter, which is used to run a function before updating, so navigation events will pass through before showing the flash message. This is useful when you want to redirect based on the fetch response:
async function submitForm(e: Event) {
const response = await fetch(new URL('/logout', $page.url), { method: 'POST' });
if (response.redirected) {
await updateFlash(page, () => goto(response.url, { invalidateAll: true }));
}
}
A common use case for flash messages is to show a toast notification, but a toast is more like an event than data that should be displayed on the page, as we've done previously. But you can use the flash
store as an event handler with a reactive statement:
src/routes/+layout.svelte
import { getFlash } from 'sveltekit-flash-message';
import { page } from '$app/stores';
import toast, { Toaster } from 'svelte-french-toast';
const flash = getFlash(page);
$: if ($flash) {
toast($flash.message, {
icon: $flash.type == 'success' ? '✅' : '❌'
});
// Clear the flash message to avoid double-toasting.
$flash = undefined;
}
When calling getFlash
, you can specify options, which will be inherited for the current route and the ones below.
const flash = getFlash(page, {
clearOnNavigate: true,
clearAfterMs: undefined,
clearArray: false,
flashCookieOptions: CookieSerializeOptions
});
You can also use initFlash
, if you don't display a flash message in a certain layout but still want to set options for the routes below:
import { initFlash } from 'sveltekit-flash-message';
import { page } from '$app/stores';
initFlash(page, {
clearAfterMs: 10000
});
If true
(the default), the flash message will be removed when navigating to a different route.
Can be set to a number of milliseconds before the flash message is automatically set to undefined
.
If you specify App.PageData['flash']
as an array, the library will concatenate messages into the array instead of replacing them. But if you always want to clear the previous messages for arrays, set the clearArray
option to true
. If your flash message isn't an array, this option will have no effect.
You can change the options for the cookie being sent, like this on the server:
import { loadFlash, flashCookieOptions } from 'sveltekit-flash-message/server';
flashCookieOptions.sameSite = 'lax';
export const load = loadFlash(async (event) => {
// ...load function...
});
And correspondingly, on the client (in a top-level component):
import { initFlash } from 'sveltekit-flash-message';
initFlash(page, {
flashCookieOptions: { sameSite: 'lax' }
});
All options can be found in the cookie npm package. Default options for the flash cookie are:
{
path: '/',
maxAge: 120,
sameSite: 'strict',
httpOnly: false // Setting this to true will most likely break things client-side.
}
The name of the cookie, flash
, cannot be changed currently, let me know if that's inconvenient. ⚡
Since the flash message is transferred in a cookie, it can be easily tampered with, so don't trust its content. Treat it like you do with any user data - hanging from a ten-foot pole over a fiery pit. 🔥 So never use {@html}
to display it, and if you need to persist it for some reason, make sure you validate it.
The sister library to sveltekit-flash-message is Superforms, the all-in-one solution for forms in SvelteKit. You can use them together without any extra work, but there are options for closer integration, found here on the Superforms website.
If you're using +hooks.server.ts/js
, or anywhere you have access to response
, calling response.headers.set('set-cookie', ...)
will discard the flash message cookie. You must use response.headers.append
instead.
In SvelteKit, links are preloaded on hover for increased responsiveness of the app. This can have the side-effect of accidentally setting a flash cookie, if a flash message redirect is made in a load function, and the user hovers over a link leading to it, so it is preloaded. To prevent this, set the data-sveltekit-preload-data="tap"
attribute on links where a redirect could happen in the load function.
The only thing you need to do when upgrading to 1.x is to remove all calls to updateFlash
in use:enhance
.
<form
method="POST"
- use:enhance={() =>
- ({ update }) =>
- updateFlash(page, update)}
+ use:enhance
>
loadFlashMessage
is deprecated and renamed to loadFlash
.initFlash
is only needed for configuration, getFlash
can be used directly in most cases.If you've added the beforeNavigate
snippet that clears the flash message after navigation, it's now automatic and can be removed. (It can be prevented by setting the clearOnNavigate
option to false
.)
If you're using the snippet for clearing the message after a certain amount of time, you can remove it and use the clearAfterMs
option instead.
Please open a github issue for suggestions, if you find a bug or have feedback in general!