Basic Svelte
Introduction
Bindings
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
Because we’re using <form>
, our app works even if the user doesn’t have JavaScript (which happens more often than you probably think). That’s great, because it means our app is resilient.
Most of the time, users do have JavaScript. In those cases, we can progressively enhance the experience, the same way SvelteKit progressively enhances <a>
elements by using client-side routing.
Import the enhance
function from $app/forms
...
src/routes/+page
<script>
import { enhance } from '$app/forms';
let { data, form } = $props();
</script>
<script lang="ts">
import { enhance } from '$app/forms';
let { data, form } = $props();
</script>
...and add the use:enhance
directive to the <form>
elements:
src/routes/+page
<form method="POST" action="?/create" use:enhance>
src/routes/+page
<form method="POST" action="?/delete" use:enhance>
And that’s all it takes! Now, when JavaScript is enabled, use:enhance
will emulate the browser-native behaviour except for the full-page reloads. It will:
- update the
form
prop - invalidate all data on a successful response, causing
load
functions to re-run - navigate to the new page on a redirect response
- render the nearest error page if an error occurs
Now that we’re updating the page rather than reloading it, we can get fancy with things like transitions:
src/routes/+page
<script>
import { fly, slide } from 'svelte/transition';
import { enhance } from '$app/forms';
let { data, form } = $props();
</script>
<script lang="ts">
import { fly, slide } from 'svelte/transition';
import { enhance } from '$app/forms';
let { data, form } = $props();
</script>
src/routes/+page
<li in:fly={{ y: 20 }} out:slide>...</li>
previous next
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<script>
let { data, form } = $props();
</script>
<div class="centered">
<h1>todos</h1>
{#if form?.error}
<p class="error">{form.error}</p>
{/if}
<form method="POST" action="?/create">
<label>
add a todo:
<input
name="description"
value={form?.description ?? ''}
autocomplete="off"
required
/>
</label>
</form>
<ul class="todos">
{#each data.todos as todo (todo.id)}
<li>
<form method="POST" action="?/delete">
<input type="hidden" name="id" value={todo.id} />
<span>{todo.description}</span>
<button aria-label="Mark as complete"></button>
</form>
</li>
{/each}
</ul>
</div>
<style>
.centered {
max-width: 20em;
margin: 0 auto;
}
label {
width: 100%;
}
input {
flex: 1;
}
span {
flex: 1;
}
button {
border: none;
background: url(./remove.svg) no-repeat 50% 50%;
background-size: 1rem 1rem;
cursor: pointer;
height: 100%;
aspect-ratio: 1;
opacity: 0.5;
transition: opacity 0.2s;
}
button:hover {
opacity: 1;
}
.saving {
opacity: 0.5;
}
</style>