Learn SvelteKit - Part 4: Form Actions
Learn SvelteKit
You are currently reading a the last installment of the Learn SvelteKit blog series. This article explains form handling in SvelteKit, including form actions setup, progressive enhancement, and SQLite integration. This series as a whole teaches you the core SvelteKit API you need to be productive building projects.
Form Actions
The next concept to review in this beginners guide are forms.
Forms are an essential part of every web application, they are the “native” way to capture user information and perform data mutation.
The form
element is the way to denote that the page will have a list of form controls. This element can use some attributes to describe the form
behavior.
action
This attribute defines the location where the form will send the collected data.method
Is the way to define what HTTP method will be used to send the data to theaction
location.
SvelteKit uses this as the default way to handle communication between front-end and back-end. SvelteKit defines the concept as Form Actions
These are a set of functions defines inside a server side code like +page.server.js
that declares how the data will be captured and what to do with the data. The function should be placed as part of an object named actions
that needs to be exported:
The above is an example of a form action that:
- retrieves the form data sent using
formData()
- Gets the fields from the
formData
Map. The fields name are the same used in thename
attribute in the html form - Execute a back-end operation like saving the data into a database
- Triggers an error if the user was not found, marking the error as 401.
- return success if all went good
But why are we doing it this way? Well, the other option is more verbose that will be to create an API endpoint that is triggered when it receives a POST request, this can be done by creating a +server.ts
file at the route and exporting a function named POST
.
The
actions
object exported from the server side code can hold many actions to handle many forms. To do that you’ll have to define multiple keys inside the object and remove thedefault
action. If you name your actions, for exampleaddTodo
instead ofdefault
theaction
attribute of the form will have to be replaced by adding the name of the action to perform:action="?/addTodo
Form Actions is a simpler way to handle a form directly in the server side code, the communication between this back-end code and the corresponding page/svelte component is handled by SvelteKit, all you need to do is to create a form in the +page.svelte
file like the following
That form will automatically (and natively) communicate with the default form action exported from the sibling +page.server.ts
in there you have access to all the required server side code to perform any operation you would need.
Form Actions feels magical but they’re just a URL that invokes a function.
But, the default behavior of forms is to send the data and refresh the page, that doesn’t feel right nowadays, that is why SvelteKit offers a way to perform progressive enhancement
Ready to pick up the pace?
Enter your email and receive regular updates on our latest articles and courses
We're here to help.
Sign up for our FREE email course
- ⭐️ Portfolio Building: Learn how to build a badass developer portfolio so you can land that next job
Progressive enhancement
The default behavior of form and form actions is to work without requiring JavaScript, just simple HTML form that send data over the wire.
This makes your application more resilient to the many things that can go wrong on different devices. But, if all goes well, the experience of using forms without JavaScript is not desirable, how can you improve it?
SvelteKit comes with a svelte action named enhance
that does exactly what the name implies.
It enhances the user experience of the form, by just adding the action into the form tag you have a fully working form that will not refresh the page when you send the data, but if for some reason the JavaScript is not loaded yet, it will still work.
When the form is submitted the use:enhance
action takes over performing the following
- Update the
form
property and$page.form
and$page.status
stores. - Reset the
<form>
element and rerun theload
function associated to the page
The default behavior of the action can also be customize to your liking. The action accepts a callback function that will run before the form is submitted.
This function returns an asynchronous function meant to run after the form is submitted. Through this function input
argument you have access to:
action
to get access to the URL details to which the form is postedcancel
a method to call to prevent form submissionformElement
gives you direct access to the<form>
elementformData
references theFormData
object as the data that is about to be submittedsubmitter
is theHTMLElement
that caused the form to be submitted
The arguments for the return function are
action
again, it gives you access to the URL detailsform
gives you access to the<form>
elementresult
anActionResult
object that will give you access to the response dataupdate
is a function that runs the regular logic of the form, if you don’t call it you’ll have to execute the logic by yourself.
The first thing that most developer will want to add to any form is a loading state, how can you do that for this form?. By simple creating a loading
state variable that will be updated by the submit function used by the enhance
action.
Let’s store new todo items in a database
The form actions are exported from the +page.server.js
file allow you to perform server side code with ease, like accessing a database instance
Remember from previous chapter, the
+page.server.js
file execute code only on the server and+page.js
file execute the code both server and client
Let’s use what we know until now continue working in the TODO application allowing the user to, create a todo item, store that into a database and also retrieve all the items to show them in the page.
First, let’s check the current state of the application, the file tree should look something like this:
Let’s open the root +page.svelte
file to add the corresponding form, it should look the same as the previous example.
Then, create the server side file +page.server.js
let’s add a basic load
function and the default form action as seen before.
You can also name the files as
+page.server.ts
to work with Typescript
The code of this file is also the same as seen before:
- a
load
function that returns dummy content - an
actions
object that defines thedefault
action, it reads the data from the form and perform a function namedsaveTodoItem
that you need to implement.
The idea now is to be able to store the data received by the action into a database, for this example I choose to use sqlite
but you can choose anything you want and will behave very similar.
The next steps will be:
- install dependencies
pnpm add better-sqlite3
- Create the sqlite database through the cli
- Create a new file to hold the database setup and logic under
src/lib/sqlite.js
To setup the database and tables that the application will use just open the CLI, make sure you are located at the root of the project and execute:
That will create a table inside the sqlite database named items
with the following keys: id
as primary key and title
and description
as text and finally priority
as integer, same as the fields that are shown in the form.
With the table created, time to go back to the code, create the file src/lib/sqlite.js
this file will hold the database setup and helper function to store and retrieve information
The file export 3 functions that allows you to insert items into the database, retrieve all of them and retrieve one identified by the id.
Now, you can use them inside the +page.server.js
file by importing the saveTodoItem
function from $lib/sqlite
to be able to save the data and also importing getTodoItems
to use it in the load
function, now the file will looks like this
Now, last step will be to update the page component to list the items returned by the load function by adding a table at the bottom like the following
This table will render the data that comes inside the data
property, it will also show you a link to navigate to that item page.
Do you remember that in previous chapter you created a dynamic route under todo/[id]
?, Let’s work on that page to retrieve just one todo item.
The current status of that page is the following
This route defines a +page.js
file but that runs both on the server and client, we need to run only server code to retrieve the database data, rename that file to +page.server.js
and let’s add the logic to get the data
Straightforward code, just grab the id
from the url parameters (the dynamic part) and use that to retrieve the item from the database with the helper function created before.
Now, on the page side, let’s display the information.
And that is all you need.
Now, the challenge for this step will be to update this page to allow the users to update a todo item, that means:
- Adding a form that shows as default values the current state of the todo item
- Adding a new form action to handle the update
- show the updated data in the page
You can also make the pages prettier by adding styling using the <style>
tag, TailwindCSS classes, or anything else you’d like.
At this point you now all that you need to kick start your journey developing applications with SvelteKit, there are more to uncover as you progress with it so stay tuned for more articles where we will delve into more advanced topics.
Ready to pick up the pace?
Enter your email and receive regular updates on our latest articles and courses
We're here to help.
Sign up for our FREE email course
- ⭐️ Portfolio Building: Learn how to build a badass developer portfolio so you can land that next job