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
In the chapter on loading data, we saw how to get data from the server to the browser. Sometimes you need to send data in the opposite direction, and that’s where <form>
— the web platform’s way of submitting data — comes in.
Let’s build a todo app. We’ve already got an in-memory database set up in src/lib/server/database.js
, and our load
function in src/routes/+page.server.js
uses the cookies
API so that we can have a per-user todo list, but we need to add a <form>
to create new todos:
<h1>todos</h1>
<form method="POST">
<label>
add a todo:
<input
name="description"
autocomplete="off"
/>
</label>
</form>
<ul class="todos">
If we type something into the <input>
and hit Enter, the browser makes a POST request (because of the method="POST"
attribute) to the current page. But that results in an error, because we haven’t created a server-side action to handle the POST request. Let’s do that now:
import * as db from '$lib/server/database.js';
export function load({ cookies }) {
// ...
}
export const actions = {
default: async ({ cookies, request }) => {
const data = await request.formData();
db.createTodo(cookies.get('userid'), data.get('description'));
}
};
The request
is a standard Request object; await request.formData()
returns a FormData
instance.
When we hit Enter, the database is updated and the page reloads with the new data.
Notice that we haven’t had to write any fetch
code or anything like that — data updates automatically. And because we’re using a <form>
element, this app would work even if JavaScript was disabled or unavailable.
<script>
let { data } = $props();
</script>
<div class="centered">
<h1>todos</h1>
<ul class="todos">
{#each data.todos as todo (todo.id)}
<li>
{todo.description}
</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>