Basic Svelte
Introduction
Bindings
Classes and styles
Advanced Svelte
Advanced reactivity
Motion
Advanced bindings
Advanced transitions
Context API
Special elements
<script module>
Next steps
Basic SvelteKit
Introduction
Routing
Loading data
Headers and cookies
Shared modules
API routes
$app/state
Errors and redirects
Advanced SvelteKit
Page options
Link options
Advanced routing
Advanced loading
Environment variables
Conclusion
With use:enhance
, we can go further than just emulating the browser’s native behaviour. By providing a callback, we can add things like pending states and optimistic UI. Let’s simulate a slow network by adding an artificial delay to our two actions:
export const actions = {
create: async ({ cookies, request }) => {
await new Promise((fulfil) => setTimeout(fulfil, 1000));
...
},
delete: async ({ cookies, request }) => {
await new Promise((fulfil) => setTimeout(fulfil, 1000));
...
}
};
When we create or delete items, it now takes a full second before the UI updates, leaving the user wondering if they messed up somehow. To solve that, add some local state...
<script>
import { fly, slide } from 'svelte/transition';
import { enhance } from '$app/forms';
let { data, form } = $props();
let creating = $state(false);
let deleting = $state([]);
</script>
<script lang="ts">
import { fly, slide } from 'svelte/transition';
import { enhance } from '$app/forms';
let { data, form } = $props();
let creating = $state(false);
let deleting = $state([]);
</script>
...and toggle creating
inside the first use:enhance
:
<form
method="POST"
action="?/create"
use:enhance={() => {
creating = true;
return async ({ update }) => {
await update();
creating = false;
};
}}
>
<label>
add a todo:
<input
disabled={creating}
name="description"
value={form?.description ?? ''}
autocomplete="off"
required
/>
</label>
</form>
We can then show a message while we’re saving data:
<ul class="todos">
<!-- ... -->
</ul>
{#if creating}
<span class="saving">saving...</span>
{/if}
In the case of deletions, we don’t really need to wait for the server to validate anything — we can just update the UI immediately:
<ul class="todos">
{#each data.todos.filter((todo) => !deleting.includes(todo.id)) as todo (todo.id)}
<li in:fly={{ y: 20 }} out:slide>
<form
method="POST"
action="?/delete"
use:enhance={() => {
deleting = [...deleting, todo.id];
return async ({ update }) => {
await update();
deleting = deleting.filter((id) => id !== todo.id);
};
}}
>
<input type="hidden" name="id" value={todo.id} />
<button aria-label="Mark as complete">✔</button>
{todo.description}
</form>
</li>
{/each}
</ul>
use:enhance
is very customizable — you cancancel()
submissions, handle redirects, control whether the form is reset, and so on. See the docs for full details.
Yikes!
We couldn't start the app. Please ensure third party cookies are enabled for this site.