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
You can also add handlers that mutate data, such as POST. In most cases, you should use form actions instead — you’ll end up writing less code, and it’ll work without JavaScript, making it more resilient.
Inside the keydown event handler of the ‘add a todo’ <input>, let’s post some data to the server:
<input
type="text"
autocomplete="off"
onkeydown={async (e) => {
if (e.key !== 'Enter') return;
const input = e.currentTarget;
const description = input.value;
const response = await fetch('/todo', {
method: 'POST',
body: JSON.stringify({ description }),
headers: {
'Content-Type': 'application/json'
}
});
input.value = '';
}}
/>Here, we’re posting some JSON to the /todo API route — using a userid from the user’s cookies — and receiving the id of the newly created todo in response.
Create the /todo route by adding a src/routes/todo/+server.js file with a POST handler that calls createTodo in src/lib/server/database.js:
import { json } from '@sveltejs/kit';
import * as database from '$lib/server/database.js';
export async function POST({ request, cookies }) {
const { description } = await request.json();
const userid = cookies.get('userid');
const { id } = await database.createTodo({ userid, description });
return json({ id }, { status: 201 });
}As with load functions and form actions, the request is a standard Request object; await request.json() returns the data that we posted from the event handler.
We’re returning a response with a 201 Created status and the id of the newly generated todo in our database. Back in the event handler, we can use this to update the page:
<input
type="text"
autocomplete="off"
onkeydown={async (e) => {
if (e.key !== 'Enter') return;
const input = e.currentTarget;
const description = input.value;
const response = await fetch('/todo', {
method: 'POST',
body: JSON.stringify({ description }),
headers: {
'Content-Type': 'application/json'
}
});
const { id } = await response.json();
const todos = [...data.todos, {
id,
description
}];
data = { ...data, todos };
input.value = '';
}}
/>You should only update
datain such a way that you’d get the same result by reloading the page. Thedataprop is not deeply reactive, so you need to replace it — mutations likedata.todos = todoswill not cause a re-render.
<script>
let { data } = $props();</script>
<div class="centered">
<h1>todos</h1>
<label>
add a todo:
<input
type="text"
autocomplete="off"
onkeydown={async (e) => {if (e.key !== 'Enter') return;
const input = e.currentTarget;
const description = input.value;
// TODO handle submit
input.value = '';
}}
/>
</label>
<ul class="todos">
{#each data.todos as todo (todo.id)}<li>
<label>
<input
type="checkbox"
checked={todo.done} onchange={async (e) => {const done = e.currentTarget.checked;
// TODO handle change
}}
/>
<span>{todo.description}</span><button
aria-label="Mark as complete"
onclick={async (e) => {// TODO handle delete
}}
></button>
</label>
</li>
{/each}</ul>
</div>
<style>
.centered {max-width: 20em;
margin: 0 auto;
}
label {display: flex;
width: 100%;
}
input[type="text"] {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;
}
</style>